Saltar al contenido principal

API de monitorización

La API de monitorización refleja la superficie del dashboard Configuración → Registros y monitorización. Úsala para crear alertas desde CI, auditar activaciones con un script o enviar el historial a un SIEM.

Todos los endpoints requieren un JWT (sesión del dashboard) y el permiso settings:read o settings:write, según la acción. No se pueden llamar con una clave de API de espacio de trabajo normal: la gestión de alertas es una operación de nivel dashboard.

Todos los endpoints de esta página son relativos a la URL base: https://api-eu1.joryio.com. Consulta el Resumen de la API.

Objeto de alerta

La estructura canónica de alerta que devuelve cada endpoint CRUD:

{
"id": "ak_01HXYZ...",
"organizationId": "org_...",
"workspaceId": "ws_...",
"name": "Server rejecting payloads",
"description": "Joryio returned 5xx on ingestion.",
"enabled": true,
"direction": "inbound",
"metric": "calls",
"codes": ["5xx"],
"mode": "absolute",
"op": ">",
"threshold": "100",
"duration": "5m",
"changeDir": null,
"changeKind": null,
"vsWindow": null,
"vsComparison": "previous",
"scopeApiKeyPrefix": null,
"scopeEndpoint": null,
"scopeWebhookUrl": null,
"scopeEventName": null,
"notifyChannel": "email",
"recipients": ["ops@your-company.com"],
"webhookUrl": null,
"webhookSecret": null,
"cooldown": "10m",
"status": "healthy",
"lastTriggeredAt": null,
"snoozedUntil": null,
"createdBy": "usr_...",
"createdAt": "2026-05-29T05:00:00.000Z",
"updatedAt": "2026-05-29T05:00:00.000Z"
}

Referencia de campos

CampoTipoNotas
namecadena (1–255)Obligatorio. Se muestra en el dashboard y los emails activados.
descriptioncadena (≤2000)Opcional. Se muestra en el cuerpo del email.
enabledbooleanoDe forma predeterminada es true. Con false, el estado pasa a paused y el evaluador omite la alerta.
directioninbound | webhook | events | deliverabilityFlujo que se vigila. events cuenta eventos de clientes registrados desde la tabla events. deliverability vigila tasas de salud de mensajes (% de enviados).
metriccalls | total_calls | rps | event_count | total_events | unique_users | bounceRate | hardBounceRate | softBounceRate | complaintRate | unsubscribeRate | deliveryRateQué medir. Los tres primeros se aplican a inbound/webhook; los tres siguientes, a events; las seis métricas *Rate, a deliverability.
codescadena[]Códigos o grupos de estado HTTP (2xx, 4xx, 5xx). Solo es significativo con metric: calls.
modeabsolute | changeModelo de umbral. deliverability siempre es absolute.
op> | <Solo modo absoluto. Dirección del umbral.
thresholdcadena (numérica)Obligatorio. Se almacena como cadena numérica. Para deliverability, un porcentaje (por ejemplo, "5" = 5%).
duration1m | 5m | 10m | 30m | 1hSolo modo absoluto. Ventana de incumplimiento sostenido. Para deliverability, la ventana retrospectiva de la tasa: usa 1h | 4h | 1d | 7d.
changeDirincreased | decreasedSolo modo de cambio. Dirección del cambio.
changeKindpercent | valueSolo modo de cambio. Interpreta threshold como porcentaje o recuento absoluto.
vsWindow15m | 1h | 4h | 1d | 7dSolo modo de cambio. Tamaño de la ventana de comparación.
vsComparisonprevious | last_week | average | same_weekday_medianSolo modo de cambio. Línea base de comparación: previous = la ventana inmediatamente anterior (predeterminada, y la menos indulgente: un domingo tranquilo se lee como caída frente al sábado); last_week = la misma ventana de hace 7 días, tiene en cuenta la estacionalidad pero es un solo día, así que uno atípico envenena la comparación; average = la MEDIA de la misma ventana durante los últimos avgDays días (2-30, 7 por defecto), suaviza el ruido pero un día excepcional eleva la base durante todo ese periodo; same_weekday_median = la MEDIANA de la misma ventana 7/14/21/28 días atrás - recomendado para alertas de caída porcentual: tiene en cuenta la estacionalidad Y no se mueve por una campaña, un artículo o un Black Friday. Necesita datos en al menos dos de las cuatro semanas; si no, la alerta informa "not enough history yet" y no se dispara.
scopeApiKeyPrefixcadena | nullLimita a una clave de API concreta. Usa el prefijo visible de la clave (por ejemplo, jry_live_98f31a72). Solo entrante.
scopeEndpointcadena | nullLimita a una ruta concreta (por ejemplo, /users/:id). Usa la forma canonizada. Solo entrante.
scopeWebhookUrlcadena | nullLimita a una URL de webhook concreta. La cadena de consulta se elimina antes de comparar. Solo saliente.
scopeEventNamecadena | nullDirección de eventos. Qué event_name contar. null = contar todos los eventos. Se ignora con metric: total_events.
notifyChannelemail | webhookCómo se entrega la alerta. De forma predeterminada es email.
recipientscadena[] (1–20)Direcciones de email notificadas al activarse. Obligatorio con notifyChannel: email.
webhookUrlcadena | nullURL de destino del POST. Obligatoria con notifyChannel: webhook.
webhookSecretcadena | nullSecreto de firma HMAC-SHA256 opcional. Al configurarlo, las solicitudes incluyen el encabezado X-Joryio-Signature.
cooldown5m | 10m | 30m | 1hTiempo mínimo entre nuevas activaciones.
statushealthy | triggered | snoozed | pausedEstado de ejecución. Es de solo lectura desde la API; usa endpoints de posponer/reanudar para cambiarlo.

Endpoints

Enumerar alertas

GET /monitoring/alerts

Devuelve todas las alertas del espacio de trabajo actual, de la más reciente a la más antigua.

Respuesta: 200 OK - MonitoringAlert[]

Obtener una alerta

GET /monitoring/alerts/:id

Respuesta: 200 OK - MonitoringAlert, o 404 si el ID no pertenece a este espacio de trabajo.

Crear una alerta

POST /monitoring/alerts
Content-Type: application/json

{
"name": "5xx error rate",
"direction": "inbound",
"metric": "calls",
"codes": ["5xx"],
"mode": "absolute",
"op": ">",
"threshold": "100",
"duration": "5m",
"recipients": ["ops@example.com"],
"cooldown": "10m"
}

Los campos name, direction, metric, mode y threshold siempre son obligatorios. Los campos específicos de canal y modo se validan semánticamente:

  • El canal de email (notifyChannel: email, el predeterminado) requiere al menos una entrada en recipients.

  • El canal de webhook (notifyChannel: webhook) requiere un webhookUrl http(s) válido; recipients es opcional. webhookSecret es opcional.

  • Los campos específicos de cada modo se validan según mode (por ejemplo, no se puede establecer op en el modo de cambios; vsComparison solo se aplica en ese modo).

  • Para direction: events, usa scopeEventName para contar un evento concreto, u omítelo para contarlos todos. metric: total_events siempre cuenta todos los eventos, independientemente de scopeEventName.

  • Para direction: deliverability, usa mode: absolute con una de las métricas *Rate, un op, un threshold porcentual y una duration de 1h/4h/1d/7d (el período de análisis). Los campos de ámbito y codes se ignoran. Si no se envió ningún mensaje durante el período, la alerta no se activa.

Respuesta: 201 Created - MonitoringAlert con id asignado.

La alerta nueva comienza con status: healthy (o paused si enabled: false) y se evaluará en el siguiente ciclo del evaluador (en un máximo de 60 segundos).

Actualizar una alerta

PATCH /monitoring/alerts/:id
Content-Type: application/json

{ "threshold": "200" }

Todos los campos son opcionales. Envía solo los que quieras cambiar. Cambiar enabled a false mueve la alerta a paused; volver a true la devuelve a healthy (el siguiente ciclo de evaluación la activará de nuevo si la métrica sigue superando el umbral).

Respuesta: 200 OK - MonitoringAlert actualizado.

Eliminar una alerta

DELETE /monitoring/alerts/:id

Elimina la alerta de forma permanente. Sus filas de historial también se eliminan en cascada.

Respuesta: 200 OK - { "ok": true }.

Posponer una alerta

POST /monitoring/alerts/:id/snooze
Content-Type: application/json

{ "window": "1h" }

window es opcional. Si se indica, la alerta pasa a snoozed, se define snoozedUntil y se reanuda automáticamente al terminar el período. Sin window, la alerta pasa a paused de forma indefinida.

Valor de windowComportamiento
1hPosponer durante 1 hora.
4hPosponer durante 4 horas.
24hPosponer durante 24 horas.
until_morningPosponer hasta las 09:00 del servidor del día siguiente.
(omitido)Pausar de forma indefinida.

Respuesta: 200 OK - MonitoringAlert actualizado.

Reanudar una alerta

POST /monitoring/alerts/:id/resume

Elimina snoozedUntil, establece enabled: true y pasa a status: healthy. El siguiente ciclo del evaluador vuelve a comprobar la métrica y puede pasar inmediatamente la alerta a triggered si sigue superando el umbral.

Respuesta: 200 OK - MonitoringAlert actualizado.

Duplicar una alerta

POST /monitoring/alerts/:id/duplicate

Crea una alerta nueva con la misma configuración. Al nombre de la copia se le añade el sufijo (copy). La copia comienza con status: healthy y lastTriggeredAt: null, independientemente del estado de ejecución de la alerta de origen.

Respuesta: 201 Created - el nuevo MonitoringAlert.

Vista previa en vivo

POST /monitoring/preview
Content-Type: application/json

{
"direction": "inbound",
"metric": "calls",
"codes": ["5xx"],
"mode": "absolute",
"op": ">",
"threshold": "100",
"duration": "5m"
}

Evalúa la especificación de alerta proporcionada frente a los datos actuales sin guardar nada. No se crea ninguna fila de alerta ni se envía ninguna notificación. Úsalo para validar los umbrales antes de crear la alerta.

El cuerpo acepta los mismos campos de evaluación que la creación; name, recipients, enabled y cooldown no son necesarios y se ignoran.

Respuesta: 200 OK

{
"currentValue": 142,
"displayValue": "142",
"thresholdLabel": "> 100 in 5m",
"wouldFire": true
}
CampoSignificado
currentValueValor sin procesar de la fuente de métricas.
displayValueVersión legible del valor. En el modo de cambios, incluye la dirección (por ejemplo, ↓ 92%).
thresholdLabelExpresión del umbral legible y correspondiente a la regla.
wouldFiretrue si la regla se encontraría ahora en estado activado.

Listar el historial

GET /monitoring/history?alertId={id}&state={state}&limit={n}

Devuelve el registro de auditoría de las transiciones de activación/resolución, de la más reciente a la más antigua.

Parámetros de consulta:

ParámetroTipoPredeterminadoNotas
alertIdcadena-Limita el resultado a una alerta.
statefiring | resolved | snoozed-Limita el resultado a un tipo de transición.
limitentero200Límite de filas devueltas. Máximo estricto: 1000.

Respuesta: 200 OK - MonitoringAlertHistoryEvent[]

[
{
"id": "ev_...",
"alertId": "ak_...",
"alertName": "Server rejecting payloads",
"metric": "Calls returning 5xx",
"valueAtFire": "184",
"valueLabel": "184",
"thresholdLabel": "> 100 in 5m",
"state": "firing",
"resolvedAt": null,
"recipients": ["ops@example.com"],
"notificationsSent": 1,
"firedAt": "2026-05-29T14:38:00.000Z"
}
]

Las filas de historial son instantáneas: guardan el nombre, la métrica, el umbral y los destinatarios de la alerta en el momento de la transición. Cambiar el nombre o eliminar la alerta más tarde no altera el historial.

Endpoints auxiliares

Estos endpoints alimentan los selectores y la campana de notificaciones del dashboard. Todos requieren settings:read.

Listar nombres de eventos

GET /monitoring/event-names

Devuelve los nombres de evento distintos observados en el espacio de trabajo durante los últimos 30 días, ordenados por frecuencia (los 200 principales). Alimenta el selector scopeEventName de la dirección Events para que los clientes vean su propio vocabulario de eventos.

Respuesta: 200 OK

[
{ "name": "purchase_complete", "count": 18422 },
{ "name": "add_to_cart", "count": 51904 },
{ "name": "signup", "count": 1203 }
]

Listar fuentes de webhook

GET /monitoring/webhook-sources

Devuelve las URL de destino de webhook distintas configuradas en los nodos de webhook activos de los Journeys activos y en borrador del espacio de trabajo (se excluyen los canvas archivados). Alimenta el selector scopeWebhookUrl de la dirección de salida.

Respuesta: 200 OK

[
{
"url": "https://hooks.your-company.com/joryio",
"canvasId": "cv_...",
"canvasName": "Win-back flow",
"nodeId": "node_...",
"nodeLabel": "Notify CRM"
}
]

Notificaciones recientes

GET /monitoring/notifications/recent

Devuelve las activaciones de alerta más recientes del espacio de trabajo para la campana del encabezado del dashboard.

Número de activaciones sin leer

GET /monitoring/notifications/unread-count

Respuesta: 200 OK - { "count": 3 }. Es el contador de la insignia de la campana del encabezado.

Cuerpo de la notificación por webhook

Cuando cambia el estado de una alerta con notifyChannel: webhook, Joryio envía un POST HTTP a webhookUrl. A diferencia del email, que solo se envía al estado triggered, el canal webhook publica tanto en triggered como en resolved, para que el receptor pueda seguir cada incidencia de principio a fin.

Solicitud:

POST {webhookUrl}
Content-Type: application/json
User-Agent: Joryio-Monitoring/1.0
X-Joryio-Signature: {hex hmac-sha256, only when a signing secret is set}

{
"alert": "Server rejecting payloads",
"status": "triggered",
"metric": "Calls returning 5xx",
"value": 184,
"displayValue": "184",
"accountName": "Acme Inc",
"workspaceName": "Production",
"firedAt": "2026-05-29T14:38:00.000Z"
}
CampoTipoNotas
alertcadenaNombre de la alerta.
statustriggered | resolvedTransición que representa este POST.
metriccadenaEtiqueta legible de la métrica monitorizada.
accountNamecadenaCuenta (organización) a la que pertenece la alerta.
workspaceNamecadenaEspacio de trabajo al que pertenece la alerta.
valuenúmeroValor sin procesar de la métrica al producirse la transición.
displayValuecadenaValor formateado para personas (en el modo de cambios incluye la dirección, por ejemplo, ↓ 92%).
firedAtcadena (ISO 8601)Momento en que ocurrió la transición.

Verificación de firma. Cuando se define webhookSecret, Joryio calcula HMAC-SHA256(rawBody) con ese secreto como clave y lo envía como una cadena hexadecimal en minúsculas (sin prefijo) en X-Joryio-Signature. Vuelve a calcularlo sobre el cuerpo exacto de la solicitud sin procesar y compáralo con una comprobación de tiempo constante antes de confiar en el cuerpo.

Semántica de entrega. Joryio espera una respuesta 2xx. Si falla, reintenta hasta 3 veces con espera exponencial (≈0,5 s, 1 s y 2 s); cada intento tiene un tiempo de espera de 10 s. Tras 3 fallos se descarta la entrega, aunque la transición de estado permanece en el historial.

Protección frente a SSRF. La URL se valida al crear o actualizar la alerta y se vuelve a validar al enviar (el DNS puede cambiar entre la escritura y la activación). Se bloquean las entregas a direcciones privadas, link-local o de metadatos de nube. No se siguen redirecciones (maxRedirects: 0), ya que un 3xx hacia una dirección interna eludiría esa comprobación.

Fuente de métricas

Las fuentes de métricas viven en el almacén de eventos analíticos y se rellenan automáticamente. Son las mismas fuentes que usan los gráficos del dashboard, por lo que el motor de alertas y cualquier análisis personalizado comparten una única fuente de verdad.

Controles de registro por organización

El registro de solicitudes de API entrantes y el registro de entregas de webhooks salientes se pueden activar o desactivar por organización y tienen una ventana de retención configurable (de 7 a 365 días; 90 de forma predeterminada), gestionada por el equipo de Joryio en la consola de administración. Cuando se desactiva un flujo para una organización, no se escriben filas y las alertas de esa dirección dejan de evaluarse. La retención se aplica fila a fila mediante una columna delete_at (las filas existentes se completaron con ts + 90d).

api_request_logs

Una fila por cada solicitud autenticada con clave de API a la API REST de Joryio.

ColumnaTipoNotas
tsDateTimeMarca de tiempo UTC al completarse la solicitud.
organization_idStringOrganización propietaria.
workspace_idStringEspacio de trabajo propietario.
api_key_idNullable(String)UUID de la fila de la clave de API.
api_key_prefixStringPrefijo público de la clave (por ejemplo, jry_live_98f31a72).
methodLowCardinality(String)Verbo HTTP.
endpointLowCardinality(String)Ruta canonizada. Los UUID y segmentos numéricos largos se sustituyen por :id.
raw_pathStringRuta original con cadena de consulta, limitada a 512 caracteres.
statusUInt16Estado de respuesta HTTP.
duration_msUInt32Latencia en milisegundos.
request_ipNullable(String)IP de origen, tras resolver X-Forwarded-For.
  • Partición: mensual (toYYYYMM(ts)).
  • Retención: TTL por fila mediante una columna delete_at, definida como ts + retentionDays al escribir. El valor predeterminado es de 90 días y se puede configurar por organización (de 7 a 365). El registro se puede desactivar por organización.
  • Excluido: tráfico del dashboard autenticado con JWT y rutas de comprobación de estado (/health, /metrics).

webhook_delivery_logs

Una fila por cada intento de entrega de webhook saliente, tenga éxito o falle.

ColumnaTipoNotas
tsDateTimeUTC timestamp of the attempt completion.
organization_idStringOwning organization.
workspace_idStringOwning workspace.
canvas_idNullable(String)ID del User Journey de origen.
execution_idNullable(String)ID de ejecución del Journey de origen.
node_idNullable(String)ID del nodo webhook de origen.
urlStringURL de destino completa.
url_canonicalStringURL sin cadena de consulta ni barra final. Se usa en los filtros de alertas.
methodLowCardinality(String)Verbo HTTP.
statusUInt16Estado de respuesta. 0 para errores de transporte (tiempo de espera, fallo de DNS o conexión rechazada).
duration_msUInt32Latencia en milisegundos.
attemptUInt8Número de intento (1 en el primer intento).
errorNullable(String)Mensaje de error en respuestas que no son 2xx.
  • Partición: mensual.
  • Retención: TTL por fila mediante delete_at. El valor predeterminado es de 90 días y se puede configurar por organización (de 7 a 365). El registro se puede desactivar por organización.
  • Excluido: webhooks activados antes del lanzamiento de esta funcionalidad (los trabajos en cola más antiguos no incluyen los metadatos de tenant necesarios para atribuirlos).

events

La dirección Events lee la tabla existente events, la misma en la que llega cada evento de cliente rastreado, en vez de un flujo de monitorización dedicado. Se usan dos agregaciones:

MétricaConsulta
event_count / total_eventscount() over (organization_id, workspace_id, [event_name], time range).
unique_usersuniqExact(user_id) over the same filter.

scopeEventName añade un predicado event_name = …; al omitirlo, se cuentan todos los nombres de evento. Esto observa el recuento de eventos almacenados: una solicitud que Joryio acepta pero cuyo cuerpo rechaza aparece en Inbound API, no aquí.

Respuestas de error

EstadoCuándo
400Error de validación: falta un campo obligatorio, mode y op no coinciden, etc. El cuerpo de respuesta enumera los campos problemáticos.
401JWT ausente o no válido.
403El JWT es válido, pero no tiene settings:read (listar/obtener/historial/vista previa) o settings:write (crear/actualizar/eliminar/posponer/reanudar/duplicar).
404No se encontró el ID de alerta en el espacio de trabajo actual.

Ejemplo de integración

Crear, previsualizar y posponer una alerta desde la terminal:

TOKEN=eyJ...
BASE=https://api-eu1.joryio.com

# 1) Vista previa antes de guardar: ¿se activaría ahora?
curl -sX POST "$BASE/monitoring/preview" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"direction":"inbound","metric":"calls","codes":["5xx"],"mode":"absolute","op":">","threshold":"100","duration":"5m"}'
# → {"currentValue":42,"displayValue":"42","thresholdLabel":"> 100 in 5m","wouldFire":false}

# 2) Se ve bien: crea la alerta.
ALERT_ID=$(curl -sX POST "$BASE/monitoring/alerts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"5xx error rate","direction":"inbound","metric":"calls","codes":["5xx"],"mode":"absolute","op":">","threshold":"100","duration":"5m","recipients":["ops@example.com"]}' \
| jq -r .id)

# 3) Posponla durante 4 horas en una ventana de mantenimiento programada.
curl -sX POST "$BASE/monitoring/alerts/$ALERT_ID/snooze" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"window":"4h"}'