Actualizar filtro de eventos
Actualiza el nombre, la descripción o la definición completa de un filtro de eventos existente.
PATCH /api/event-criterias/:id
Actualiza un filtro de eventos existente. Los tres campos del cuerpo son opcionales: se puede cambiar solo el nombre, solo la descripción, solo la definición, o cualquier combinación.
Auth: Requerida — permiso event-criteria:update
Parámetros de ruta
| Parámetro | Tipo | Descripción |
|---|---|---|
id | UUID | ID del filtro de eventos. |
Cuerpo de la solicitud
Nivel raíz (UpdateEventCriteriaDto):
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | No | Nuevo nombre. Si se envía, no puede ser vacío ni null. |
description | string | null | No | Nueva descripción. Enviar null la deja vacía. |
queryBlocks | QueryBlockDto[] | No | Nueva definición completa. Mínimo 1 elemento si se envía. Reemplaza los bloques existentes. |
La estructura de cada bloque está en Definición.
Semántica del PATCH
El PATCH es parcial entre campos, pero total dentro de queryBlocks. Es la
distinción que decide si una actualización pierde bloques:
| Qué se envía | Qué pasa |
|---|---|
name y/o description | Se actualizan solo esos campos. Los bloques de consulta quedan intactos. |
queryBlocks | Se borran todos los bloques del filtro y se crean los enviados. No hay merge por order ni por id. |
Nada (cuerpo {}) | El filtro no cambia, salvo updatedBy y updatedAt. |
queryBlocks reemplaza la definición entera. Enviar un solo bloque en un filtro que
tenía tres deja el filtro con un bloque; los otros dos se borran. Para modificar un bloque
hay que enviar la lista completa, incluidos los que no cambian.
Los bloques enviados se persisten como bloques nuevos: sus id no son los que tenía
el filtro antes, aunque el order y el contenido coincidan. Un id de bloque no es un
identificador estable entre actualizaciones de la definición.
En toda actualización, updatedBy pasa a ser el usuario que hace la solicitud y
updatedAt se refresca.
Ejemplo
Cambiar el nombre y reemplazar la definición por una que agrega un filtro de registro
sobre items.
curl -X PATCH https://api.reten.ai/api/event-criterias/4b8e0f15-2c76-4a03-9d58-7e1f2a3b4c5d \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>" \
-H "Content-Type: application/json" \
-d '{
"name": "Ventas con ítems no-devolución",
"queryBlocks": [
{
"order": 0,
"from": { "table": "event_transactions", "joins": [] },
"select": {
"distinct": true,
"items": [
{ "kind": "column", "column": { "table": "event_transactions", "column": "event_id" } }
]
},
"where": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"column": { "table": "event_transactions", "column": "status" },
"comparisonOperator": "EQUALS",
"value": "Facturada"
},
{
"kind": "recordPredicate",
"column": { "table": "event_transactions", "column": "items" },
"subWhere": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"field": "category",
"comparisonOperator": "NOT_IN",
"value": ["devolucion"]
}
]
}
}
]
}
}
]
}'import axios from 'axios';
const id = '4b8e0f15-2c76-4a03-9d58-7e1f2a3b4c5d';
const body = {
name: 'Ventas con ítems no-devolución',
queryBlocks: [
{
order: 0,
from: { table: 'event_transactions', joins: [] },
select: {
distinct: true,
items: [
{ kind: 'column', column: { table: 'event_transactions', column: 'event_id' } },
],
},
where: {
kind: 'group',
logicalOperator: 'AND',
children: [
{
kind: 'predicate',
column: { table: 'event_transactions', column: 'status' },
comparisonOperator: 'EQUALS',
value: 'Facturada',
},
{
kind: 'recordPredicate',
column: { table: 'event_transactions', column: 'items' },
subWhere: {
kind: 'group',
logicalOperator: 'AND',
children: [
{
kind: 'predicate',
field: 'category',
comparisonOperator: 'NOT_IN',
value: ['devolucion'],
},
],
},
},
],
},
},
],
};
const response = await axios.patch(
`https://api.reten.ai/api/event-criterias/${id}`,
body,
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const updated = response.data;Respuesta 200 OK
Devuelve el filtro actualizado como IEventCriteriaDetail — la misma forma que
GET /event-criterias/:id, con los bloques ya
persistidos y sus id nuevos.
{
"id": "4b8e0f15-2c76-4a03-9d58-7e1f2a3b4c5d",
"projectId": "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34",
"name": "Ventas con ítems no-devolución",
"description": "Ventas facturadas con al menos una línea que no es devolución",
"isActive": true,
"ownerId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"updatedBy": "0f0e0d0c-0b0a-4090-8070-605040302010",
"createdAt": "2026-07-12T11:05:19.220Z",
"updatedAt": "2026-07-18T16:41:02.907Z",
"queryBlocks": [
{
"id": "c8d9e0f1-2a3b-4c5d-8e6f-7a8b9c0d1e2f",
"order": 0,
"setOperator": null,
"from": { "table": "event_transactions", "joins": [] },
"select": {
"distinct": true,
"items": [
{ "kind": "column", "column": { "table": "event_transactions", "column": "event_id" } }
]
},
"timeWindow": null,
"where": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"column": { "table": "event_transactions", "column": "status" },
"comparisonOperator": "EQUALS",
"value": "Facturada"
},
{
"kind": "recordPredicate",
"column": { "table": "event_transactions", "column": "items" },
"subWhere": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"field": "category",
"comparisonOperator": "NOT_IN",
"value": ["devolucion"]
}
]
}
}
]
},
"groupBy": null,
"having": null
}
]
}Reglas de validación
La definición se valida solo si el cuerpo trae queryBlocks, y antes de tocar nada: un
reemplazo que no compila a SQL no borra los bloques que ya existían. Una actualización que
solo cambia name o description no revalida la definición guardada.
El catálogo completo de reglas y sus mensajes exactos está en Definición → Reglas de validación.
Respuestas de error
| Estado | Descripción |
|---|---|
400 | El id no es un UUID válido, el DTO es inválido (tipo incorrecto, enum fuera de rango, queryBlocks vacío, propiedad desconocida) o una regla de negocio incumplida. |
401 | Falta o es inválido el Authorization: Bearer. |
403 | El usuario no tiene el permiso event-criteria:update, o el x-tenant-id no está autorizado. |
404 | No existe un filtro de eventos con ese id en el tenant (Event criteria not found). |