Previsualizar
Ejecuta un filtro de eventos contra BigQuery y devuelve una muestra acotada de filas más el total que matchea.
Previsualizar
Ejecuta un filtro de eventos contra BigQuery y devuelve una muestra de las filas reales que selecciona, más el total. Son dos rutas con la misma respuesta: una ejecuta una definición enviada en el cuerpo y la otra un filtro ya guardado.
| Ruta | Origen de la definición | Código de éxito |
|---|---|---|
POST /api/event-criterias/preview-from-body | El cuerpo | 201 |
POST /api/event-criterias/:id/preview | El filtro guardado | 201 |
Auth: Requerida — permiso event-criteria:view-sql en ambas rutas
A diferencia de Validar, que hace un
dry-run, previsualizar ejecuta el query: consume cuota de BigQuery y su costo aparece
en bytesProcessed.
POST /api/event-criterias/preview-from-body
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
queryBlocks | QueryBlockDto[] | Sí | Mínimo 1 elemento. La definición a ejecutar. |
limit | number | No | Tamaño de la muestra. Entero entre 1 y 500. Default 100. |
La estructura de cada bloque está en Definición.
Este endpoint recibe solo la definición y el limit: name, description y format
no forman parte del cuerpo y enviarlos produce un 400 por propiedad desconocida.
Ejemplo
curl -X POST https://api.reten.ai/api/event-criterias/preview-from-body \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>" \
-H "Content-Type: application/json" \
-d '{
"limit": 50,
"queryBlocks": [
{
"order": 0,
"from": { "table": "event_transactions", "joins": [] },
"select": {
"distinct": true,
"items": [
{ "kind": "column", "column": { "table": "event_transactions", "column": "event_id" } },
{ "kind": "column", "column": { "table": "event_transactions", "column": "event_date" } }
]
},
"timeWindow": {
"timeOperator": "IN_THE_LAST",
"reference": { "table": "event_transactions", "column": "event_date" },
"unit": "DAYS",
"value": 30
},
"where": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"column": { "table": "event_transactions", "column": "status" },
"comparisonOperator": "EQUALS",
"value": "Facturada"
}
]
}
}
]
}'import axios from 'axios';
const body = {
limit: 50,
queryBlocks: [
{
order: 0,
from: { table: 'event_transactions', joins: [] },
select: {
distinct: true,
items: [
{ kind: 'column', column: { table: 'event_transactions', column: 'event_id' } },
{ kind: 'column', column: { table: 'event_transactions', column: 'event_date' } },
],
},
timeWindow: {
timeOperator: 'IN_THE_LAST',
reference: { table: 'event_transactions', column: 'event_date' },
unit: 'DAYS',
value: 30,
},
where: {
kind: 'group',
logicalOperator: 'AND',
children: [
{
kind: 'predicate',
column: { table: 'event_transactions', column: 'status' },
comparisonOperator: 'EQUALS',
value: 'Facturada',
},
],
},
},
],
};
const response = await axios.post(
'https://api.reten.ai/api/event-criterias/preview-from-body',
body,
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const { rows, totalCount, bytesProcessed } = response.data;POST /api/event-criterias/:id/preview
Ejecuta un filtro ya guardado. La definición se lee del filtro y se valida antes de ejecutarla.
Parámetros de ruta
| Parámetro | Tipo | Descripción |
|---|---|---|
id | UUID | ID del filtro de eventos. |
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
limit | number | No | Tamaño de la muestra. Entero entre 1 y 500. Default 100. |
El cuerpo no lleva la definición: el filtro guardado es la definición. Enviar
queryBlocks produce un 400 por propiedad desconocida.
Ejemplo
curl -X POST https://api.reten.ai/api/event-criterias/6d2a9c41-7e35-48b0-b1c9-0f8e7d6c5b4a/preview \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>" \
-H "Content-Type: application/json" \
-d '{ "limit": 50 }'import axios from 'axios';
const id = '6d2a9c41-7e35-48b0-b1c9-0f8e7d6c5b4a';
const response = await axios.post(
`https://api.reten.ai/api/event-criterias/${id}/preview`,
{ limit: 50 },
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const { rows, totalCount, bytesProcessed } = response.data;limit — tamaño de la muestra
limit acota solo cuántas filas trae la respuesta en rows (la muestra), no el
universo que el filtro selecciona.
- Rango: entero 1..500 (menor a 1 o mayor a 500 ⇒
400). - Default:
100cuando se omite.
Previsualizar corre dos consultas sobre la misma definición: una
SELECT … LIMIT <limit> para la muestra y un SELECT COUNT(*) para el total. Por eso
podés ver rows.length = 50 con totalCount = 4821: rows es un vistazo,
totalCount es la cantidad real de filas que el filtro selecciona.
Respuesta
Status 201 Created en ambas rutas.
| Campo | Tipo | Descripción |
|---|---|---|
rows | Record<string, unknown>[] | Muestra de filas reales, acotada por limit. Las columnas dependen del select de la definición (y de sus alias); por eso rows no tiene una forma fija. |
totalCount | number | Total de filas que el filtro selecciona, sin acotar por limit. |
bytesProcessed | number | Bytes procesados por BigQuery en la corrida. Suma los bytes de ambas consultas (muestra + conteo). |
Ejemplo
Respuesta 201 al ejemplo de arriba (limit: 50). El filtro selecciona 4821 eventos;
rows muestra los primeros 50 (aquí recortados a 3 por brevedad), cada uno con las
columnas proyectadas:
{
"rows": [
{ "event_id": "evt_9f3a1c", "event_date": { "value": "2026-07-14T18:02:11.000Z" } },
{ "event_id": "evt_2b7e04", "event_date": { "value": "2026-07-14T17:45:03.000Z" } },
{ "event_id": "evt_c081ff", "event_date": { "value": "2026-07-13T09:21:58.000Z" } }
],
"totalCount": 4821,
"bytesProcessed": 5242880
}Las claves de cada objeto en rows (event_id, event_date) salen del select de la
definición: otra definición produce otras columnas.
Una columna TIMESTAMP no llega como string: llega envuelta en
{ "value": "<ISO 8601>" }. La fecha ISO está en row.event_date.value, no en
row.event_date. Los demás tipos del catálogo llegan como escalares JSON.
Reglas de validación
Antes de ejecutar, la definición pasa por las dos capas de validación. La validación de
negocio corre en ambas rutas: preview-from-body valida la definición del cuerpo, y
:id/preview valida el filtro guardado antes de ejecutarlo.
El catálogo completo de reglas y sus mensajes exactos está en Definición → Reglas de validación.
Si la definición supera las dos capas pero la ejecución en BigQuery falla —por exceder
el límite de bytes facturables (10 GB), agotar el timeout de 30 s, o cualquier otro error
del motor— la respuesta también es 400, con un message del tipo
Query execution failed: <detalle>.
Respuestas de error
| Estado | Descripción |
|---|---|
400 | DTO inválido (limit fuera de 1..500, tipo incorrecto, enum fuera de rango, propiedad desconocida), una regla de negocio incumplida, o un fallo de ejecución en BigQuery (Query execution failed: …). En :id/preview, también si el id no es un UUID válido. |
401 | Falta o es inválido el Authorization: Bearer. |
403 | El usuario no tiene el permiso event-criteria:view-sql, o el x-tenant-id no está autorizado. |
404 | En :id/preview, no existe un filtro de eventos con ese id en el tenant (Event criteria not found). En ambas rutas, si el tenant no tiene dataset configurado. |