Reten Docs
Filtros de eventos

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.

RutaOrigen de la definiciónCódigo de éxito
POST /api/event-criterias/preview-from-bodyEl cuerpo201
POST /api/event-criterias/:id/previewEl filtro guardado201

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

CampoTipoRequeridoDescripción
queryBlocksQueryBlockDto[]SíMínimo 1 elemento. La definición a ejecutar.
limitnumberNoTamañ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ámetroTipoDescripción
idUUIDID del filtro de eventos.

Cuerpo de la solicitud

CampoTipoRequeridoDescripción
limitnumberNoTamañ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: 100 cuando 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.

CampoTipoDescripción
rowsRecord<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.
totalCountnumberTotal de filas que el filtro selecciona, sin acotar por limit.
bytesProcessednumberBytes 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

EstadoDescripción
400DTO 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.
401Falta o es inválido el Authorization: Bearer.
403El usuario no tiene el permiso event-criteria:view-sql, o el x-tenant-id no está autorizado.
404En :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.