Event Filters
Select events with a query-builder, without writing SQL, and generate, validate and preview the resulting query.
Overview
An event filter (event criteria) is a set of events selected with a
query-builder —table → columns → filters → time window → aggregations— without
writing SQL by hand.
The definition is assembled by composing:
| Piece | What it provides |
|---|---|
| Query blocks | One or more blocks; each block starts from a table of the events catalog and defines on it columns, filters, time and aggregations. |
| Column filters | Conditions over a scalar column with a comparison operator (EQUALS, IN, BETWEEN, …). |
| Record filters | Conditions over a RECORD REPEATED column (attributes, items): there is at least one element that satisfies the sub-filter. |
| Related table filters | Conditions over a semi-join declared by the catalog (products): there is at least one match in the related table. |
| Time window | Narrows the block to a time period (IN_THE_LAST 30 DAYS, SINCE, …). |
| Aggregations | Summarizes block columns into a metric (COUNT, SUM, AVG, …). |
The full tree —with the three where leaves and all their fields— is documented once
in Definition; the endpoint pages
reference it instead of repeating it.
No identity column
An event filter projects events, not subjects: there is no mandatory identity
column and the generated SQL returns subjectColumn: null. That is the structural
difference with audiences, which do have to expose
their identity in at least one block.
Endpoints
Catalog
GET /tables and GET /tables/:tableName/columns — what can be filtered.
List
GET /event-criterias — paginated list with search by name.
Get
GET /event-criterias/:id — full detail of a filter.
Create
POST /event-criterias — creates a filter from its definition.
Update
PATCH /event-criterias/:id — updates an existing filter.
Delete
DELETE /event-criterias/:id — archives a filter.
Generate SQL
POST /generate-sql and GET /:id/sql — obtains the SQL for a definition.
Validate
POST /validate-from-body — validates a definition against BigQuery.
Preview
POST /preview-from-body and POST /:id/preview — runs a bounded sample.
Compute format
GET /event-criterias/:id?format=compute — flat list of conditions.
Base URL and environments
All routes hang off the global prefix /api under the
/event-criterias resource (for example POST /api/event-criterias).
Reten Environments
| Environment | Admin Panel | API Base URL |
|---|---|---|
| Development | app-development.reten.ai | https://api-development.reten.ai |
| Staging | app-staging.reten.ai | https://api-staging.reten.ai |
| Production | app.reten.ai | https://api.reten.ai |
Use the admin panel URL to manage API keys and dispatch configurations. Replace BASE_URL in code examples with the API base URL for your environment.
Authentication
All endpoints require a JWT Bearer token, the x-tenant-id header
that identifies the tenant on whose schema the request operates, and the
x-project-id header that identifies the project the request works in (see
Authentication → Project Context).
| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer <access_token> |
x-tenant-id | Yes | Tenant UUID |
x-project-id | Yes | Project UUID |
-H "Authorization: Bearer <access_token>"
-H "x-tenant-id: <tenant-id>"
-H "x-project-id: <project-id>"Permissions
Each route requires a specific permission:
| Permission | Endpoints |
|---|---|
event-criteria:view | GET /event-criterias, GET /event-criterias/:id, GET /tables, GET /tables/:tableName/columns |
event-criteria:create | POST /event-criterias |
event-criteria:update | PATCH /event-criterias/:id |
event-criteria:delete | DELETE /event-criterias/:id |
event-criteria:view-sql | POST /generate-sql, GET /:id/sql, POST /validate-from-body, POST /preview-from-body, POST /:id/preview |
The catalog (/tables and /tables/:tableName/columns) requires
event-criteria:view, the same read permission of the resource: whoever can see a
filter can see what it is built with.
Status codes
The read-only POST endpoints (generate-sql, validate-from-body,
preview-from-body, :id/preview) respond 201 Created —not 200—, even
though they don't create any resource.
| Method + route | Code | Response body |
|---|---|---|
POST /event-criterias (create) | 201 | The created filter |
GET /event-criterias | 200 | Paginated list |
GET /event-criterias/:id | 200 | The filter |
GET /event-criterias/tables | 200 | Catalog tables |
GET /tables/:tableName/columns | 200 | The table's columns and semi-joins |
PATCH /event-criterias/:id | 200 | The updated filter |
DELETE /event-criterias/:id | 204 | (no body) |
POST /generate-sql | 201 | Generated SQL |
GET /:id/sql | 200 | Generated SQL |
POST /validate-from-body | 201 | Validation result |
POST /preview-from-body | 201 | Sample result |
POST /:id/preview | 201 | Sample result |
Error format
Validation, authorization and resolution errors follow the API's standard error format:
{
"statusCode": 400,
"message": ["name should not be empty"],
"error": "Bad Request"
}On DTO validation errors, message is an array of strings (one entry per broken
rule). Validation is strict: undeclared properties are rejected, so sending an
unknown field produces a 400.
Common errors in this API:
400 Bad Request— invalid definition or DTO (missing field, wrong type, enum out of range, unknown property), or a broken business rule.401 Unauthorized— theAuthorization: Beareris missing or invalid.403 Forbidden— the user lacks the required permission, or thex-tenant-idis not authorized.404 Not Found— the requestediddoes not exist in the tenant (Event criteria not found).
Pagination
The list wraps results in PaginatedResponseDto<T>:
{
"data": [],
"pagination": {
"page": 1,
"per_page": 25,
"total": 0,
"total_pages": 0,
"has_next": false,
"has_previous": false
}
}The fields inside pagination use snake_case (per_page, total_pages,
has_next, has_previous), unlike the rest of the resource which uses camelCase.
The detail of the pagination parameters is documented in List.
Enum glossary
Quick reference catalog. The field where each one appears and its exact shape are in Definition.
SetOperatorEnum
Combination of query blocks within the same filter (a block's setOperator).
UNION_DISTINCT·UNION_ALL·INTERSECT_DISTINCT
LogicalOperatorEnum
Chaining of predicates within a group of the filter tree.
AND·OR
ComparisonOperatorEnum
Operator of a predicate over a column or over a sub-field.
EQUALS·NOT_EQUALS·GREATER_THAN·LESS_THAN·GREATER_THAN_OR_EQUAL·LESS_THAN_OR_EQUAL·CONTAINS·NOT_CONTAINS·STARTS_WITH·ENDS_WITH·IS_NULL·IS_NOT_NULL·IN·NOT_IN·BETWEEN
AggregationOperatorEnum
Aggregation function over an operand (a column, * or an arithmetic expression).
COUNT·SUM·AVG·MIN·MAX
ArithmeticOperatorEnum
Operator of an arithmetic expression inside an aggregate's operand. SAFE_DIVIDE yields NULL
when the denominator is zero.
ADD·SUBTRACT·MULTIPLY·SAFE_DIVIDE
TimeOperatorEnum
Operator of the time window (timeWindow).
ALL_TIME·IN_THE_LAST·IN_THE_FIRST·SINCE·BETWEEN
IN_THE_FIRST is not supported in this resource: the "in the first N" window
anchors to the lifetime origin of a subject, and an event filter has no subject.
Sending it responds 400 with
timeWindow IN_THE_FIRST is not supported: the catalog does not declare a lifetime origin.
TimeUnitEnum
Unit of the time window.
DAYS·WEEKS·MONTHS·YEARS
JoinTypeEnum
Type of join between catalog tables.
INNER·LEFT·RIGHT·FULL
CatalogFieldTypeEnum
Data type of a scalar column of the events catalog. It determines which comparison operators it accepts.
STRING·TIMESTAMP·BOOLEAN·FLOAT64·INT64·NUMERIC·BIGNUMERIC
Differences from audiences
Both resources share the query-builder, but they are not symmetric. If you arrive from audiences, these are the assumptions that do not carry over:
| Aspect | Audiences | Event filters |
|---|---|---|
| Composition with other criteria | composedWith | Does not exist |
| Identity column | Mandatory in ≥1 block; 400 if missing | Not applicable — it projects events, not subjects |
subjectColumn of the SQL | The identity column | Always null |
IN_THE_FIRST | Supported | 400 — with no subject there is no lifetime origin to anchor to |
| Validate by id | POST /:id/validate | Does not exist; only POST /validate-from-body |
| List counts | queryBlockCount, compositionCount | conditionCount — where leaves of every block |
| Catalog — columns | isIdentity and isDefaultTimeReference per column | Neither; the time reference belongs to the table (defaultTimeReferenceColumn) |
| Catalog — semi-joins | Not declared | products → product_properties |
| Filter tree leaves | predicate, recordPredicate | predicate, recordPredicate, semiJoinPredicate |
finalCteName of the SQL | The one from the audiences domain | CTE_VALID_EVENTS |