Reten Docs

Audience catalog

Tables, columns and repeated records available to build an audience.

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 and RECORD REPEATED columns.

Without the catalog the definition is not buildable: there is no other way to know which is the identity column of a table, which TIMESTAMP column it suggests as the time reference, or that attributes is a repeated record and not a comparable column.

Auth: Required — audience-criteria:view permission on both routes


GET /api/audience-criterias/tables

Lists the catalog tables on which an audience 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/audience-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/audience-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 IAudienceTable:

FieldTypeDescription
tableNamestringTable name; it is the value that goes in from.table and in ColumnRefDto.table.
identityColumnstringOptional. Identity column of the table (for example user_id). A table that only serves to join may not have one.
defaultTimeReferenceColumnstringOptional. TIMESTAMP column suggested as the time window's reference. Another TIMESTAMP column can be chosen.
requiresInnerJoinbooleantrue if the table can only participate as the main table of the from or as the target of an INNER JOIN.
[
  {
    "tableName": "user_properties",
    "identityColumn": "user_id",
    "defaultTimeReferenceColumn": "sign_up_date",
    "requiresInnerJoin": false
  },
  {
    "tableName": "event_transactions",
    "identityColumn": "user_id",
    "defaultTimeReferenceColumn": "event_date",
    "requiresInnerJoin": false
  },
  {
    "tableName": "user_segment_relation",
    "identityColumn": "user_id",
    "requiresInnerJoin": true
  },
  {
    "tableName": "user_seller_relation",
    "identityColumn": "user_id",
    "requiresInnerJoin": true
  }
]

The identityColumn is the column that at least one query block has to project in its select: a definition that does not expose it is rejected with 400. The detail is in Identity column.

A table with requiresInnerJoin: true restricts the entire block: if it takes part in a block, all the joins of that block have to be INNER. The signal is in the catalog so that the block is built right from the start, instead of discovering it with a 400 when validating.


GET /api/audience-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

ParameterTypeDescription
tableNamestringTable name, as returned by GET /tables.

Example

curl https://api.reten.ai/api/audience-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/audience-criterias/tables/event_transactions/columns',
  { headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const columns = response.data;

Response 200 OK

An array of IAudienceColumn: a discriminated union by kind with two variants.

Narrow by kind before reading type or fields. Only the scalar variant carries type; only record carries fields. Reading column.type without discriminating returns undefined for a repeated record.

  • kind: "scalar" — a column that can be filtered directly:

    FieldTypeDescription
    columnNamestringColumn 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.
    isIdentitybooleantrue if it is the identity column of the table. At most one column per table has it.
    isDefaultTimeReferencebooleantrue if it is the table's suggested time reference. It is a suggestion: the window can be anchored on another TIMESTAMP column.
  • kind: "record" — a RECORD REPEATED column, filterable only with a record filter:

    FieldTypeDescription
    columnNamestringName of the repeated column.
    kind"record"Discriminator.
    fieldsIAudienceColumn[]Filterable sub-fields, relative to the record. It may contain other record.
    isIdentityfalseAlways false: a repeated record cannot be the identity of the table.
    isDefaultTimeReferencefalseAlways false: a repeated record cannot anchor the time window.

The fields of a record use the same scalar | record union, so the rule of narrowing by kind applies at every level.

A sub-field of a record never carries isIdentity or isDefaultTimeReference as true, not even if it has the same name as the identity column of the table. The two roles belong to the table over its first-level columns.

Example response

GET /api/audience-criterias/tables/event_transactions/columns:

[
  {
    "columnName": "user_id",
    "kind": "scalar",
    "type": "STRING",
    "isIdentity": true,
    "isDefaultTimeReference": false
  },
  {
    "columnName": "event_date",
    "kind": "scalar",
    "type": "TIMESTAMP",
    "isIdentity": false,
    "isDefaultTimeReference": true
  },
  {
    "columnName": "event_id",
    "kind": "scalar",
    "type": "STRING",
    "isIdentity": false,
    "isDefaultTimeReference": false
  },
  {
    "columnName": "status",
    "kind": "scalar",
    "type": "STRING",
    "isIdentity": false,
    "isDefaultTimeReference": false
  },
  {
    "columnName": "coupon_code",
    "kind": "scalar",
    "type": "STRING",
    "isIdentity": false,
    "isDefaultTimeReference": false
  },
  {
    "columnName": "pricing.final_amount",
    "kind": "scalar",
    "type": "FLOAT64",
    "isIdentity": false,
    "isDefaultTimeReference": false
  },
  {
    "columnName": "attributes",
    "kind": "record",
    "fields": [
      {
        "columnName": "key",
        "kind": "scalar",
        "type": "STRING",
        "isIdentity": false,
        "isDefaultTimeReference": false
      },
      {
        "columnName": "value",
        "kind": "scalar",
        "type": "STRING",
        "isIdentity": false,
        "isDefaultTimeReference": false
      }
    ],
    "isIdentity": false,
    "isDefaultTimeReference": false
  }
]

How each variant is used in the definition

VariantWhere it goes
scalarColumnRefDto.column of a predicate leaf, of the select, of the groupBy, of an aggregate, of a join's on or of the time window's reference.
recordcolumn of a recordPredicate leaf; its fields go in the field of the subWhere leaves.

A record column cannot be compared or projected: using it in the select, in the groupBy, in an aggregate, in a join's on or in a predicate leaf returns 400. Its only use is the record filter.

Error Responses

StatusDescription
401Missing or invalid Authorization: Bearer.
403The user does not have the audience-criteria:view permission, or the x-tenant-id is not authorized.
404Only on /tables/:tableName/columns: the table is not in the catalog (La tabla no existe o no está en el catálogo de audiencias.), or the tenant has no dataset configured (Dataset not configured for tenant <id>).
500Only 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 audiencias.).

GET /api/audience-criterias/tables does not query the tenant's dataset, so it only returns 200, 401 or 403.