Event catalog
Tables, columns, repeated records and semi-joins available to build an event filter.
Catalog
The catalog answers what can be filtered. There are two routes: one lists the available tables and the other, for a table, everything it offers —scalar columns, RECORD REPEATED columns and semi-joins against related tables.
Without the catalog the definition is not
buildable: there is no other way to know that attributes is a repeated record, that
items[] has sub-fields, or that products reaches the product_properties table.
Auth: Required — event-criteria:view permission on both routes
GET /api/event-criterias/tables
Lists the catalog tables on which an event filter can be built. The catalog is identical across tenants: this route does not query the tenant's dataset.
Example
curl https://api.reten.ai/api/event-criterias/tables \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>"import axios from 'axios';
const response = await axios.get(
'https://api.reten.ai/api/event-criterias/tables',
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const tables = response.data;Response 200 OK
An array of IEventTable:
| Field | Type | Description |
|---|---|---|
tableName | string | Table name; it is the value that goes in from.table and in ColumnRefDto.table. |
defaultTimeReferenceColumn | string | Optional. TIMESTAMP column suggested as the time window's reference. Another TIMESTAMP column can be chosen. |
requiresInnerJoin | boolean | true if the table can only participate as the main table of the from or as the target of an INNER JOIN. |
[
{
"tableName": "event_transactions",
"defaultTimeReferenceColumn": "event_date",
"requiresInnerJoin": false
}
]The tables in this catalog do not declare identityColumn: an event filter projects
events, not subjects. The field exists in the response shape for parity with the
audience catalog, but here it always comes
absent.
GET /api/event-criterias/tables/:tableName/columns
Returns everything the catalog offers about a table. Unlike /tables, this route
reconciles the catalog against the real schema of the tenant's dataset before
responding.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
tableName | string | Table name, as returned by GET /tables. |
Example
curl https://api.reten.ai/api/event-criterias/tables/event_transactions/columns \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>"import axios from 'axios';
const response = await axios.get(
'https://api.reten.ai/api/event-criterias/tables/event_transactions/columns',
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const entries = response.data;Response 200 OK
An array of IEventCatalogEntry: a discriminated union by kind with three
variants.
Narrow by kind before reading type or fields. Only the scalar variant carries
type; only record and semiJoin carry fields. Reading entry.type without
discriminating returns undefined for the other two variants.
-
kind: "scalar"— a column that can be filtered directly:Field Type Description columnNamestring Column name. For leaves of a non-repeated STRUCT it is the dotted path ( pricing.final_amount).kind"scalar"Discriminator. typeCatalogFieldTypeEnumCatalog type; it determines which comparison operators it admits. -
kind: "record"— a RECORD REPEATED column, filterable only with a record filter:Field Type Description columnNamestring Name of the repeated column. kind"record"Discriminator. fieldsIEventColumn[]Filterable sub-fields, relative to the record. It may contain other record. -
kind: "semiJoin"— a related table reachable with a related table filter:Field Type Description kind"semiJoin"Discriminator. semiJoinNamestring Name of the semi-join; it is the value that goes in semiJoinPredicate.semiJoin.targetTablestring Related table against which the filter is resolved. fieldsIEventColumn[]Filterable fields of the related table.
The fields of a record and of a semiJoin use the same scalar | record union,
so the rule of narrowing by kind applies at every level.
Example response
GET /api/event-criterias/tables/event_transactions/columns (trimmed in the scalar
columns for brevity; the records and the semi-join are complete):
[
{ "columnName": "event_id", "kind": "scalar", "type": "STRING" },
{ "columnName": "event_date", "kind": "scalar", "type": "TIMESTAMP" },
{ "columnName": "user_id", "kind": "scalar", "type": "STRING" },
{ "columnName": "status", "kind": "scalar", "type": "STRING" },
{ "columnName": "coupon_code", "kind": "scalar", "type": "STRING" },
{ "columnName": "pricing.net_amount", "kind": "scalar", "type": "FLOAT64" },
{ "columnName": "pricing.final_amount", "kind": "scalar", "type": "FLOAT64" },
{ "columnName": "payment_details.method", "kind": "scalar", "type": "STRING" },
{ "columnName": "payment_details.date", "kind": "scalar", "type": "TIMESTAMP" },
{ "columnName": "shipping_address.region", "kind": "scalar", "type": "STRING" },
{
"columnName": "attributes",
"kind": "record",
"fields": [
{ "columnName": "key", "kind": "scalar", "type": "STRING" },
{ "columnName": "value", "kind": "scalar", "type": "STRING" }
]
},
{
"columnName": "items",
"kind": "record",
"fields": [
{ "columnName": "item_id", "kind": "scalar", "type": "STRING" },
{ "columnName": "name", "kind": "scalar", "type": "STRING" },
{ "columnName": "price", "kind": "scalar", "type": "FLOAT64" },
{ "columnName": "quantity", "kind": "scalar", "type": "FLOAT64" },
{ "columnName": "category", "kind": "scalar", "type": "STRING" }
]
},
{
"kind": "semiJoin",
"semiJoinName": "products",
"targetTable": "product_properties",
"fields": [
{ "columnName": "sku", "kind": "scalar", "type": "STRING" },
{ "columnName": "name", "kind": "scalar", "type": "STRING" },
{ "columnName": "categorization.family_name", "kind": "scalar", "type": "STRING" },
{ "columnName": "categorization.subfamily_name", "kind": "scalar", "type": "STRING" },
{ "columnName": "categorization.category_name", "kind": "scalar", "type": "STRING" },
{ "columnName": "categorization.brand_name", "kind": "scalar", "type": "STRING" },
{ "columnName": "categorization.collection_name", "kind": "scalar", "type": "STRING" },
{ "columnName": "commercial.base_price", "kind": "scalar", "type": "FLOAT64" },
{ "columnName": "composition.is_pack", "kind": "scalar", "type": "BOOLEAN" },
{ "columnName": "composition.is_combo", "kind": "scalar", "type": "BOOLEAN" }
]
}
]How each variant is used in the definition
| Variant | Where it goes |
|---|---|
scalar | ColumnRefDto.column of a predicate leaf, of the select, of the groupBy, of an aggregate or of the time window's reference. |
record | column of a recordPredicate leaf; its fields go in the field of the subWhere leaves. |
semiJoin | semiJoin of a semiJoinPredicate leaf; its fields go in the field of the subWhere leaves. |
The targetTable of a semi-join is not a catalog table: it does not appear in
GET /tables, it cannot go in the from nor be the target of a join. It is only reached
with a semiJoinPredicate leaf.
A semi-join is omitted from the response if the tenant's dataset does not have its
related table: it is not an error, it is that this tenant does not have that data source.
The absence of the semiJoin from the catalog is the signal that the filter is not
available.
Error Responses
| Status | Description |
|---|---|
401 | Missing or invalid Authorization: Bearer. |
403 | The user does not have the event-criteria:view permission, or the x-tenant-id is not authorized. |
404 | Only on /tables/:tableName/columns: the table is not in the catalog (La tabla no existe o no está en el catálogo de eventos.), or the tenant has no dataset configured (Dataset not configured for tenant <id>). |
500 | Only on /tables/:tableName/columns: the real schema of the dataset does not satisfy the catalog (El schema de datos no satisface el catálogo de eventos.). |
GET /api/event-criterias/tables does not query the tenant's dataset, so it only returns
200, 401 or 403.