Reten Docs
Audiencias

Crear Audience Criteria

Crea un criterio de audiencia a partir de su definición completa (bloques de consulta, filtros, ventana temporal, agregaciones y composiciones).

POST /api/audience-criterias

Crea un Audience Criteria a partir de su definición completa. Es el endpoint central del recurso: recibe todo el árbol de la definición —bloques de consulta, proyección, filtros, ventana temporal, cruces, agregaciones y composiciones con otras audiencias— y lo persiste. La operación es transaccional: el criterio, sus query blocks y sus composiciones se crean juntos, o no se crea nada.

Auth: Requerida — permiso audience-criteria:create

Cuerpo de la solicitud

Nivel raíz (CreateAudienceCriteriaDto):

CampoTipoRequeridoDescripción
namestringSíNombre del criterio. No puede ser vacío.
descriptionstringNoDescripción de texto libre. Si se omite, la respuesta la devuelve como null.
queryBlocksQueryBlockDto[]SíMínimo 1 elemento. Cada bloque ≈ un statement SQL completo.
composedWithCreateCompositionDto[]NoCompone este criterio con otras audiencias vía operaciones de conjunto.

queryBlocks[] — bloque de consulta

Cada elemento de queryBlocks (QueryBlockDto) es un statement SQL completo:

CampoTipoRequeridoDescripción
ordernumber (entero)SíPosición del bloque; entero ≥ 0.
setOperatorSetOperatorEnumNoCombina este bloque con el anterior. Ausente en el primer bloque; obligatorio en el resto (ver reglas).
fromFromDtoSíFuente de datos: tabla principal + joins.
selectSelectDtoSíProyección (columnas y/o agregaciones).
timeWindowTimeWindowDtoNoVentana temporal del bloque (unión discriminada por timeOperator).
whereWhereDtoSíÁrbol de predicados. Obligatorio aunque sea un grupo vacío.
groupByColumnRefDto[]NoColumnas de agrupación.
havingHavingDtoNoFiltro sobre agregados.

El setOperator de un query block combina bloques dentro del mismo criterio; no debe confundirse con el setOperator de una composición, que combina criterios enteros. Son dos niveles distintos de operación de conjunto.

ColumnRefDto — referencia a columna

Se usa en todo el árbol (select, filtros, group by, joins, agregados, ventana temporal):

CampoTipoRequeridoDescripción
tablestringSíTabla del catálogo. No vacío.
columnstringSíColumna. No vacío. Puede ser un path de STRUCT (p. ej. "pricing.final_amount").

Toda columna se cualifica siempre con su table para evitar ambigüedad.

from — fuente de datos (FromDto)

CampoTipoRequeridoDescripción
tablestringSíTabla principal del catálogo. No vacío.
joinsJoinDto[]NoCruces con otras tablas. Default [].
unnestsColumnRefDto[]NoColumnas repetidas declaradas como fuente de filas (una fila por elemento). Una audiencia no las admite: omite el campo o envía [] (el default); una lista no vacía da 400.

Cada JoinDto:

CampoTipoRequeridoDescripción
typeJoinTypeEnumSíINNER | LEFT | RIGHT | FULL.
tablestringSíTabla a unir (del catálogo). No vacío.
onJoinPredicateDto[]SíCondiciones de unión.

Cada JoinPredicateDto es un par de columnas: { left: ColumnRefDto, right: ColumnRefDto }.

select — proyección (SelectDto)

CampoTipoRequeridoDescripción
distinctbooleanSíAplica SELECT DISTINCT.
items(SelectColumnItem | SelectAggregateItem)[]SíNo vacío. Unión discriminada por el campo kind.

Los items se discriminan por kind:

  • Columna — kind: "column":

    CampoTipoRequeridoDescripción
    kind"column"SíDiscriminador.
    columnColumnRefDtoSíColumna proyectada.
    aliasstringNoDebe matchear ^[A-Za-z_][A-Za-z0-9_]*$.
  • Agregado — kind: "aggregate":

    CampoTipoRequeridoDescripción
    kind"aggregate"SíDiscriminador.
    aggregateAggregateDtoSíExpresión de agregación.
    aliasstringSíAquí el alias es obligatorio. Mismo regex de identificador.

AggregateDto:

CampoTipoRequeridoDescripción
aggregationOperatorAggregationOperatorEnumSíCOUNT | SUM | AVG | MIN | MAX.
operandobjectSíLo que se agrega. Unión discriminada por kind: column, star o arithmetic (ver abajo).
distinctbooleanNoAplica DISTINCT dentro del agregado.

operand —lo que se agrega— se discrimina por kind:

kindCampos adicionalesSignificado
columncolumn: ColumnRefDtoUna columna del catálogo.
star(ninguno)*: cuenta filas. Solo es válido con COUNT y sin distinct (COUNT(DISTINCT *) no está permitido).
arithmeticarithmeticOperator: ArithmeticOperatorEnum, left, rightExpresión binaria. Cada lado es otro operando: una column o un arithmetic anidado (nunca star).

ArithmeticOperatorEnum: ADD | SUBTRACT | MULTIPLY | SAFE_DIVIDE (una división que da NULL cuando el denominador es cero, en vez de hacer fallar la consulta). Las columnas de una expresión aritmética tienen que ser numéricas (INT64, NUMERIC, BIGNUMERIC, FLOAT64), y las expresiones se anidan hasta 3 niveles de arithmetic, contando el exterior.

where — árbol de predicados (WhereDto)

El where es un árbol recursivo. El nodo raíz es siempre un grupo, y sus hojas son de dos tipos:

kindQué filtra
groupNo es una hoja: agrupa hijos con AND / OR. Se pueden anidar grupos.
predicateUna columna escalar de una tabla del from.
recordPredicateUna columna RECORD REPEATED: existe ≥1 elemento que cumple el sub-filtro.
Grupo — kind: "group"
CampoTipoRequeridoDescripción
kind"group"SíDiscriminador.
logicalOperatorLogicalOperatorEnum (AND | OR)SíConectivo entre los hijos.
children(Grupo | Hoja)[]SíHijos del grupo; pueden ser grupos anidados.

logicalOperator es obligatorio siempre en un grupo, pero solo tiene efecto con 2 o más hijos. Con menos de 2 hijos su valor es indiferente (genera el mismo SQL), pero igual debe enviarse. Un where sin filtros se expresa como un grupo vacío: { "kind": "group", "logicalOperator": "AND", "children": [] }.

Hoja de columna — kind: "predicate"

Compara una columna escalar cualificada por su tabla.

CampoTipoRequeridoDescripción
kind"predicate"SíDiscriminador.
columnColumnRefDtoSíColumna del predicado.
comparisonOperatorComparisonOperatorEnumSíOperador de comparación.
valuePredicateValueDependeSu forma depende del operador.
{
  "kind": "predicate",
  "column": { "table": "user_properties", "column": "country" },
  "comparisonOperator": "IN",
  "value": ["CL", "MX"]
}
Hoja de registro — kind: "recordPredicate"

Filtra por una columna RECORD REPEATED (un array de structs, como attributes). Significa existe al menos un elemento del array que cumple el sub-filtro.

CampoTipoRequeridoDescripción
kind"recordPredicate"SíDiscriminador.
columnColumnRefDtoSíColumna RECORD REPEATED de una tabla del from.
subWhereSub-árbol relativoSíCondiciones sobre los sub-campos del elemento. No puede quedar vacío.
{
  "kind": "recordPredicate",
  "column": { "table": "event_transactions", "column": "attributes" },
  "subWhere": {
    "kind": "group",
    "logicalOperator": "AND",
    "children": [
      { "kind": "predicate", "field": "key", "comparisonOperator": "EQUALS", "value": "canal" },
      { "kind": "predicate", "field": "value", "comparisonOperator": "EQUALS", "value": "web" }
    ]
  }
}

Las condiciones del subWhere se evalúan sobre un mismo elemento del array, no sobre el array entero. El ejemplo de arriba selecciona filas que tienen un atributo cuya key es canal y cuyo value es web — no filas que tengan un atributo con esa key y otro, distinto, con ese value.

Qué columnas admiten un filtro de registro lo dice el catálogo: las que vienen con kind: "record".

Sub-árbol relativo

El subWhere es un grupo AND/OR cuyas hojas se resuelven contra los sub-campos que el registro declara.

La diferencia con el árbol de primer nivel es una sola, y es la fuente de error más común:

Dentro de un sub-árbol relativo los operandos no llevan tabla: se escribe "field": "key", no "column": { "table": "attributes", "column": "key" }. La hoja usa la clave field (un string), no column.

Hojas admitidas dentro de un sub-árbol relativo:

  • Hoja escalar — { "kind": "predicate", "field": string, "comparisonOperator": …, "value"?: … }. El field debe ser un sub-campo escalar del registro.
  • Registro anidado — { "kind": "recordPredicate", "field": string, "subWhere": … }. Un registro dentro de otro registro; se anida sin límite declarado, siempre que el catálogo declare cada nivel.
  • Grupo — la misma forma de grupo, para combinar las hojas anteriores con AND/OR.
Forma de value según comparisonOperator
OperadoresForma de valueEjemplo
IS_NULL, IS_NOT_NULLSin value (se omite)(se omite)
IN, NOT_INLista no vacía ScalarValue[]["CL", "MX"]
BETWEENPar [min, max][10, 20]
El resto (EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH)Escalar único"activo" · 42

ScalarValue = string | number | boolean. Además, cada operador debe ser compatible con el tipo de la columna (ver reglas de validación).

groupBy — columnas de agrupación

Lista opcional de ColumnRefDto[]. Cuando el bloque agrega (declara groupBy o tiene un agregado en el select), el groupBy debe coincidir exactamente con las columnas no agregadas del select (ver reglas de validación).

having — filtro sobre agregados (HavingDto)

Misma estructura recursiva que where (grupos AND/OR anidados y misma regla de value por operador), pero la hoja opera sobre un agregado, no sobre una columna:

  • Predicado (hoja) — { kind: "predicate", aggregate: AggregateDto, comparisonOperator: ComparisonOperatorEnum, value?: PredicateValue }.
  • Grupo — idéntico al de where.

timeWindow — ventana temporal (TimeWindowDto)

Unión discriminada por timeOperator. Acota el bloque a un período de tiempo sobre una columna TIMESTAMP del catálogo (reference). Es un campo hermano del where, no un nodo del árbol de filtros.

timeOperatorCampos adicionales
ALL_TIME(ninguno) — sin acotar por tiempo.
IN_THE_LASTreference: ColumnRefDto, unit: TimeUnitEnum, value: entero > 0
IN_THE_FIRSTreference: ColumnRefDto, unit: TimeUnitEnum, value: entero > 0
SINCEreference: ColumnRefDto, from: string (YYYY-MM-DD)
BETWEENreference: ColumnRefDto, from: string (YYYY-MM-DD), to: string (YYYY-MM-DD)

TimeUnitEnum: DAYS | WEEKS | MONTHS | YEARS.

Semántica de negocio de cada operador:

  • ALL_TIME — sin ventana; considera todo el histórico.
  • IN_THE_LAST — ventana rodante relativa a "ahora": los últimos value unit (p. ej. últimos 30 DAYS). Se recomputa en cada corrida.
  • IN_THE_FIRST — ventana relativa al origen del ciclo de vida del usuario (hoy, su fecha de alta): los primeros value unit de vida de cada usuario. Se ancla por usuario, no a una fecha fija.
  • SINCE — desde la fecha civil from (inclusiva) en adelante.
  • BETWEEN — entre from y to, ambas fechas civiles inclusivas de su día completo.

Las fechas civiles YYYY-MM-DD (SINCE, BETWEEN) se resuelven al inicio del día en la zona horaria del tenant. El detalle de zona horaria es interno; basta con enviar la fecha civil.

composedWith[] — composición con otras audiencias

Cada elemento (CreateCompositionDto) combina este criterio con otra audiencia vía una operación de conjunto:

CampoTipoRequeridoDescripción
operandCriteriaIdstring (UUID)SíId de la otra audiencia (operando). Debe existir.
setOperatorSetOperatorEnumSíUNION_DISTINCT | UNION_ALL | INTERSECT_DISTINCT.

Reglas de validación

La solicitud pasa por dos capas de validación, ambas devuelven 400 Bad Request:

  1. Validación del DTO (estructura, tipos, enums, campos requeridos). En este caso message es un arreglo de strings y se rechazan propiedades desconocidas.
  2. Validación de negocio (el criterio debe generar SQL válido y coherente contra el catálogo). En este caso message es un string único con el mensaje de la regla incumplida. La validación de negocio se detiene en el primer invariante violado.

Reglas de negocio verificadas (todas devuelven 400, con el mensaje exacto del API):

ReglaMensaje
La tabla debe pertenecer al catálogo.Table "<t>" is not in the catalog
Cada (table, column) debe pertenecer al catálogo.Column "<t>.<c>" is not in the catalog
Una columna repetida no puede usarse fuera de un filtro de registro.La columna "<t>.<c>" es una columna repetida y no puede usarse en <contexto>
Una hoja de columna exige una columna escalar.La columna "<t>.<c>" es una columna repetida: no admite un filtro de columna
Un filtro de registro exige una columna repetida.La columna "<t>.<c>" no es una columna repetida: no admite un filtro de registro
El sub-campo de un filtro de registro debe existir en el registro.El sub-campo "<f>" no existe en el registro
Una hoja escalar del sub-árbol no puede apuntar a un sub-campo repetido.El sub-campo "<f>" es una columna repetida: usá un filtro de registro anidado
Un registro anidado exige un sub-campo repetido.El sub-campo "<f>" no es una columna repetida
El sub-árbol de un filtro de registro no puede quedar vacío.Un filtro de registro no puede quedar sin condiciones
Una hoja del árbol debe declarar su operando.Una hoja del filtro no declara una columna
El operando star (*) solo es válido con COUNT.'*' is only valid with COUNT, not <op>
COUNT(DISTINCT *) no está permitido.COUNT(DISTINCT *) is not allowed
star no puede ser lado de una expresión aritmética.'*' sólo vale como operando directo de COUNT, no como lado de una expresión aritmética
Las columnas de una expresión aritmética tienen que ser numéricas.El operando "<t>.<c>" es de tipo <type>; una expresión aritmética sólo admite operandos numéricos (INT64, NUMERIC, BIGNUMERIC, FLOAT64)
Una expresión aritmética se anida hasta 3 niveles como máximo.La expresión aritmética supera la profundidad máxima de 3 niveles
Toda columna referenciada debe pertenecer a una tabla del from (principal ∪ joins).Column "<t>.<c>" references a table that is not in the query block FROM
Al agregar, toda columna no agregada del select debe estar en el groupBy.Column "<t>.<c>" must appear in GROUP BY when the operation aggregates
Toda columna del groupBy debe ser una columna no agregada del select.Column "<t>.<c>" in GROUP BY must be a non-aggregated column of the SELECT
having exige contexto de agregación (groupBy o un agregado en el select).HAVING requires an aggregation context (GROUP BY or an aggregate in the SELECT)
El criterio debe proyectar su columna de identidad (p. ej. user_id) como columna.Criteria requires at least one operation that selects its identity column (e.g. user_id)
El primer bloque (order=0) no lleva setOperator.First operation (order=0) must not have a setOperator
Todo bloque posterior (order>0) debe llevar setOperator.Operation with order=<n> must have a setOperator
El operador debe ser compatible con el tipo de la columna/agregado.Operator <op> is not allowed for <type> operands
Operador escalar con value faltante o lista.Operator <op> requires a single value
IN/NOT_IN con value no-lista o vacía.Operator <op> requires a non-empty list of values
BETWEEN sin exactamente dos valores.Operator <op> requires exactly two values
La reference del timeWindow debe estar en el catálogo.timeWindow reference "<t>.<c>" is not in the catalog
La reference del timeWindow debe ser una columna TIMESTAMP.timeWindow reference "<t>.<c>" must be a TIMESTAMP column
La reference del timeWindow no puede ser una columna repetida.La referencia temporal "<t>.<c>" es una columna repetida y no puede anclar una ventana de tiempo
value del timeWindow debe ser un entero positivo.timeWindow value must be a positive integer
En timeWindow BETWEEN, from debe ser anterior o igual a to.timeWindow BETWEEN requires "from" to be on or before "to"
Las fechas del timeWindow deben ser fechas de calendario válidas.timeWindow date "<d>" is not a valid calendar date

Reglas específicas de composedWith (también 400):

ReglaMensaje
Un criterio no puede referenciarse a sí mismo.An audience criteria cannot reference itself
El operando debe existir.Operand audience criteria <id> not found or deleted
No se permiten referencias circulares entre criterios.Circular reference detected

Ejemplo

Audiencia de usuarios que en los últimos 90 días completaron transacciones por más de 100000, cruzando event_transactions con user_properties para filtrar por país, e intersectada con otra audiencia existente que también proyecta solo user_id.

curl -X POST https://api.reten.ai/api/audience-criterias \
  -H "Authorization: Bearer <token>" \
  -H "x-tenant-id: <tenant-id>" \
  -H "x-project-id: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Compradores VIP (últimos 90 días)",
    "queryBlocks": [
      {
        "order": 0,
        "from": {
          "table": "event_transactions",
          "joins": [
            {
              "type": "INNER",
              "table": "user_properties",
              "on": [
                {
                  "left":  { "table": "event_transactions", "column": "user_id" },
                  "right": { "table": "user_properties", "column": "user_id" }
                }
              ]
            }
          ]
        },
        "select": {
          "distinct": false,
          "items": [
            { "kind": "column", "column": { "table": "event_transactions", "column": "user_id" } }
          ]
        },
        "timeWindow": {
          "timeOperator": "IN_THE_LAST",
          "reference": { "table": "event_transactions", "column": "event_date" },
          "unit": "DAYS",
          "value": 90
        },
        "where": {
          "kind": "group",
          "logicalOperator": "AND",
          "children": [
            {
              "kind": "predicate",
              "column": { "table": "event_transactions", "column": "status" },
              "comparisonOperator": "EQUALS",
              "value": "completed"
            },
            {
              "kind": "predicate",
              "column": { "table": "user_properties", "column": "country" },
              "comparisonOperator": "IN",
              "value": ["CL", "MX"]
            }
          ]
        },
        "groupBy": [
          { "table": "event_transactions", "column": "user_id" }
        ],
        "having": {
          "kind": "group",
          "logicalOperator": "AND",
          "children": [
            {
              "kind": "predicate",
              "aggregate": {
                "aggregationOperator": "SUM",
                "operand": { "kind": "column", "column": { "table": "event_transactions", "column": "pricing.final_amount" } }
              },
              "comparisonOperator": "GREATER_THAN",
              "value": 100000
            }
          ]
        }
      }
    ],
    "composedWith": [
      {
        "operandCriteriaId": "b2c3d4e5-6789-4abc-9def-0123456789ab",
        "setOperator": "INTERSECT_DISTINCT"
      }
    ]
  }'
import axios from 'axios';

const body = {
  name: 'Compradores VIP (últimos 90 días)',
  queryBlocks: [
    {
      order: 0,
      from: {
        table: 'event_transactions',
        joins: [
          {
            type: 'INNER',
            table: 'user_properties',
            on: [
              {
                left: { table: 'event_transactions', column: 'user_id' },
                right: { table: 'user_properties', column: 'user_id' },
              },
            ],
          },
        ],
      },
      select: {
        distinct: false,
        items: [
          { kind: 'column', column: { table: 'event_transactions', column: 'user_id' } },
        ],
      },
      timeWindow: {
        timeOperator: 'IN_THE_LAST',
        reference: { table: 'event_transactions', column: 'event_date' },
        unit: 'DAYS',
        value: 90,
      },
      where: {
        kind: 'group',
        logicalOperator: 'AND',
        children: [
          {
            kind: 'predicate',
            column: { table: 'event_transactions', column: 'status' },
            comparisonOperator: 'EQUALS',
            value: 'completed',
          },
          {
            kind: 'predicate',
            column: { table: 'user_properties', column: 'country' },
            comparisonOperator: 'IN',
            value: ['CL', 'MX'],
          },
        ],
      },
      groupBy: [{ table: 'event_transactions', column: 'user_id' }],
      having: {
        kind: 'group',
        logicalOperator: 'AND',
        children: [
          {
            kind: 'predicate',
            aggregate: {
              aggregationOperator: 'SUM',
              operand: { kind: 'column', column: { table: 'event_transactions', column: 'pricing.final_amount' } },
            },
            comparisonOperator: 'GREATER_THAN',
            value: 100000,
          },
        ],
      },
    },
  ],
  composedWith: [
    {
      operandCriteriaId: 'b2c3d4e5-6789-4abc-9def-0123456789ab',
      setOperator: 'INTERSECT_DISTINCT',
    },
  ],
};

const response = await axios.post(
  'https://api.reten.ai/api/audience-criterias',
  body,
  { headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const created = response.data;

Respuesta 201 Created

Devuelve el criterio creado como IAudienceCriteriaDetail — la misma forma que GET /audience-criterias/:id. Es el recurso base más el árbol completo: queryBlocks[] (con su id y order ya resueltos, ordenados por order ascendente) y compositions[] (con id y operandCriteriaName).

Campos base (IAudienceCriteria) — todos camelCase; los instantes se serializan a ISO 8601:

CampoTipoDescripción
idstring (UUID)Id del criterio creado.
projectIdstring (UUID)Proyecto al que pertenece el criterio.
namestring
descriptionstring | nullnull si no se envió.
isActivebooleanLos criterios se crean activos (true).
isRootbooleantrue sólo para la audiencia raíz del proyecto (ver Proyectos).
ownerIdstring (UUID)Usuario creador.
updatedBystring (UUID)Último editor (en la creación, el creador).
createdAtstring (ISO 8601)
updatedAtstring (ISO 8601)

Relaciones anidadas:

  • queryBlocks[] — cada bloque añade su id (UUID) y devuelve el árbol tal como se envió (from, select, timeWindow, where, groupBy, having). Los campos opcionales ausentes se devuelven como null (setOperator, timeWindow, groupBy, having) y from.joins como [] si no había joins; from.unnests viene siempre como [].
  • compositions[] — cada composición: id (UUID), operandCriteriaId, operandCriteriaName (nombre del operando resuelto; "" si no resuelve) y setOperator.
{
  "id": "a1b2c3d4-5678-4abc-9def-1234567890ab",
  "projectId": "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34",
  "name": "Compradores VIP (últimos 90 días)",
  "description": null,
  "isActive": true,
  "isRoot": false,
  "ownerId": "0f0e0d0c-0b0a-4090-8070-605040302010",
  "updatedBy": "0f0e0d0c-0b0a-4090-8070-605040302010",
  "createdAt": "2026-07-15T14:32:07.481Z",
  "updatedAt": "2026-07-15T14:32:07.481Z",
  "queryBlocks": [
    {
      "id": "c3d4e5f6-7890-4abc-9def-34567890abcd",
      "order": 0,
      "setOperator": null,
      "from": {
        "table": "event_transactions",
        "joins": [
          {
            "type": "INNER",
            "table": "user_properties",
            "on": [
              {
                "left":  { "table": "event_transactions", "column": "user_id" },
                "right": { "table": "user_properties", "column": "user_id" }
              }
            ]
          }
        ],
        "unnests": []
      },
      "select": {
        "distinct": false,
        "items": [
          { "kind": "column", "column": { "table": "event_transactions", "column": "user_id" } }
        ]
      },
      "timeWindow": {
        "timeOperator": "IN_THE_LAST",
        "reference": { "table": "event_transactions", "column": "event_date" },
        "unit": "DAYS",
        "value": 90
      },
      "where": {
        "kind": "group",
        "logicalOperator": "AND",
        "children": [
          {
            "kind": "predicate",
            "column": { "table": "event_transactions", "column": "status" },
            "comparisonOperator": "EQUALS",
            "value": "completed"
          },
          {
            "kind": "predicate",
            "column": { "table": "user_properties", "column": "country" },
            "comparisonOperator": "IN",
            "value": ["CL", "MX"]
          }
        ]
      },
      "groupBy": [
        { "table": "event_transactions", "column": "user_id" }
      ],
      "having": {
        "kind": "group",
        "logicalOperator": "AND",
        "children": [
          {
            "kind": "predicate",
            "aggregate": {
              "aggregationOperator": "SUM",
              "operand": { "kind": "column", "column": { "table": "event_transactions", "column": "pricing.final_amount" } }
            },
            "comparisonOperator": "GREATER_THAN",
            "value": 100000
          }
        ]
      }
    }
  ],
  "compositions": [
    {
      "id": "d4e5f6a7-8901-4abc-9def-4567890abcde",
      "operandCriteriaId": "b2c3d4e5-6789-4abc-9def-0123456789ab",
      "operandCriteriaName": "Usuarios activos",
      "setOperator": "INTERSECT_DISTINCT"
    }
  ]
}

Respuestas de error

EstadoDescripción
400DTO inválido (campo faltante, tipo incorrecto, enum fuera de rango, propiedad desconocida) o una regla de negocio incumplida (ver reglas de validación).
401Falta o es inválido el Authorization: Bearer.
403El usuario no tiene el permiso audience-criteria:create, o el x-tenant-id no está autorizado.