---
title: "Ejecutar segmento con paginación"
method: POST
path: "/segments/execute"
tags: ["Segmentation"]
---

# Ejecutar segmento con paginación

`POST /segments/execute`

## Request

- Base URL: `http://localhost:8081`
- URL: `http://localhost:8081/segments/execute`
- Auth: HTTP bearer

Ejecuta el segmento completo retornando todos los fans que cumplen los criterios.

**Paginación**:
- `page_size`: Número de fans por página (default: 100, max: 1000)
- `page`: Número de página 0-indexed (default: 0)

**Response**:
- `total_fans`: Total de fans en el segmento
- `fans`: Array de fans de la página actual
- `current_page`: Página actual (0-indexed)
- `total_pages`: Total de páginas disponibles
- `has_more`: true si hay más páginas

**Performance**:
- Primera ejecución hace COUNT para total
- Resultados se obtienen con LIMIT/OFFSET optimizado
- Recomendado usar page_size entre 50-500 según uso

La estructura de `definition` es idéntica a `/segments/preview`.

## Request body

- SegmentExecuteRequest
  - `definition` Definition, required
    - `stanzas` Stanza[], required: Stanzas unidas con AND
      - `id` string, required: UUID para identificación en UI
      - `criteria` Criterion[], required: Criterios unidos con OR
        - `id` string, required: UUID para identificación en UI
        - `type` string: Tipo de criterio (obsoleto, usar criterion_id)
        - `criterion_id` string, required: ID del criterio global
        - `operator` 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'is_true' | 'is_false' | 'before' | 'after' | 'between' | 'is' | 'is_not' | 'is_set' | 'is_not_set', required: Operadores disponibles según tipo de dato: **TEXT**: eq, neq, contains, not_contains, starts_with, ends_with, is_set, is_not_set **NUMBER**: eq, neq, gt, gte, lt, lte, is_set, is_not_set **BOOLEAN**: is_true, is_false **DATE**: before, after, between, eq, is_set, is_not_set **ENTITY**: is, is_not, is_set, is_not_set **CURRENCY**: eq, neq, gt, gte, lt, lte, is_set, is_not_set
        - `value` union, required: Valor del criterio (tipo depende del data_type)
          - string
          - number
          - boolean
        - `timeframe` union: Marco temporal para el criterio
          - TimeFrameRelativeToNow
            - `type` 'relative_to_now', required
            - `unit` 'days' | 'hours' | 'weeks' | 'months', required
            - `value_start` integer, required: Inicio del rango (0 = ahora)
            - `value_end` integer, required: Fin del rango
          - TimeFrameRelativeToExperience
            - `type` 'relative_to_experience', required
            - `experienceId` string, required: ID del evento de referencia
            - `phase` 'before_start' | 'after_start' | 'before_end' | 'after_end', required
            - `unit` 'days' | 'hours' | 'weeks', required
            - `value` integer, required
          - TimeFrameAbsolute
            - `type` 'absolute', required
            - `start_date` string, date, required: Fecha de inicio (ISO 8601)
            - `end_date` string, date, required: Fecha de fin (ISO 8601)
        - `filters` object: Filtros adicionales (ej. property_name para custom fields)
        - `modifiers` Modifiers
          - `distance` DistanceModifier
            - `radius` number, required: Radio en kilómetros
            - `unit` 'km' | 'mi'
          - `frequency` FrequencyModifier
            - `count` integer, required: Número de veces
            - `operator` 'exactly' | 'at_least' | 'at_most', required: Operador de comparación
          - `time_range` TimeRangeModifier: Ventana relativa, recalculada en cada evaluación. En criterios cuya fecha está en otra tabla (`attended_event` → `events.start_ts`) se evalúa sobre el mismo registro que cumple el criterio, en días naturales de Europe/Madrid y con ambos límites incluidos: `past` = desde las 00:00 de hoy − N unidades hasta ahora (sin eventos futuros); `future` = desde ahora hasta el final de hoy + N unidades. Semanas = 7·N días; meses y años, aritmética de calendario.
            - `value` integer, required: Valor numérico
            - `unit` 'days' | 'weeks' | 'months' | 'years', required
            - `direction` 'past' | 'future'
        - `displayValue` unknown
        - `metadata` object: Metadata adicional para UI
          - `criteriaName` string
          - `dataType` string
          - `category` string
    - `limit` integer: Límite opcional de resultados (caso de uso warm-up de campañas). Máximo 100000. Si se especifica offset, limit es obligatorio.
    - `offset` integer: Saltar N resultados antes de aplicar limit (skip+limit para warm-up). Debe ser >= 0. Requiere que limit también esté definido. Orden de aplicación: groups → offset/limit → page/page_size HTTP.
  - `groups` GroupsConfig
    - `total_groups` integer, required: Número total de grupos en los que dividir el segmento
    - `group_number` integer, required: Número del grupo a obtener (1-indexed, entre 1 y total_groups)
  - `page_size` integer: Tamaño de página
  - `page` integer: Número de página (0-indexed)

## Response `200`

Segmento ejecutado exitosamente

- ExecuteResponse
  - `success` boolean, required
  - `total_fans` integer, required: Total de fans en el segmento
  - `fans` Fan[], required: Fans de la página actual
    - `fan_id` string, required: ID del usuario
    - `first_name` string, required: Nombre
    - `last_name` string, required: Apellido
    - `email` string, email, required: Email
    - `phone` string: Teléfono
    - `dni` string: DNI/Fiscal ID
    - `email_marketing_status` 'ok' | 'no_email' | 'invalid_email' | 'opt_out' | 'unknown', nullable: Estado de consentimiento de marketing por email del fan (NUEVO). Solo presente cuando la respuesta viene del path enriquecido de contactabilidad (ver `contactable_counts` en `PreviewResponse`); ausente por completo en el path legacy (mart `user_tenants` sin las columnas de contactabilidad todavía desplegadas).
  - `current_page` integer, required: Página actual (0-indexed)
  - `total_pages` integer, required: Total de páginas
  - `has_more` boolean, required: Si hay más páginas disponibles
  - `group_metadata` GroupMetadata
    - `total_groups` integer: Número total de grupos
    - `current_group` integer: Número del grupo actual (1-indexed)
    - `total_fans_all_groups` integer: Total de fans en el segmento completo (sin filtro de grupos)
    - `avg_group_size` integer: Tamaño promedio esperado por grupo
    - `assignment_method` 'hash': Método de asignación de fans a grupos
  - `economic_data_as_of` string, date-time, nullable: Solo si la definición usa un criterio económico (#57). Cambio de origen más reciente observado a la vez en el dim de compras y en el raw (source_timestamp máximo): un máximo observado, NO una marca de agua de completitud. No garantiza que estén reflejados todos los cambios anteriores: una imagen que llega tarde o que el almacén retiene puede faltar aunque el as-of sea posterior. Hoy no existe señal de completitud. Preséntese como «último cambio observado», nunca como «datos completos hasta». null si no se sabe. En un periodo sin cambios no avanza aunque el pipeline esté vivo.
  - `economic_data_built_at` string, date-time, nullable: Solo con criterio económico. Construcción de purchase_economic_facts. Es vitalidad del pipeline; no es marca de agua de origen ni dice nada de completitud.
  - `economic_data_status` 'CURRENT' | 'STALE' | 'UNKNOWN': Solo con criterio económico. Estado de VITALIDAD, no de completitud. CURRENT: facts se reconstruyó dentro de la cadencia (no certifica que estén todos los cambios de origen); STALE si se han perdido dos o más ciclos de 15 min (el resultado se calculó igual, sobre la última foto); UNKNOWN si la frescura no se pudo leer.
  - `edition_scopes` object[]: Solo con criterio por edición (#58: edition_spent, edition_participated). Edición y revisión del vínculo usadas por cada criterio: solo miembros CONFIRMED a esa revisión. pinned = la revisión venía en filters.edition_revision; si no, era la vigente.
    - `edition_id` string
    - `revision` integer
    - `pinned` boolean

## Other responses

- `400`: Solicitud rechazada antes de ejecutar ninguna consulta. `code` es estable y legible por máquina (el texto de `error` puede cambiar); la lista es abierta, así que un cliente trata un código que no conoce como un fallo de la solicitud, no de la definición del segmento. Algunos códigos: - `INVALID_PAGINATION`: `page`/`page_size` (o `after_user_id` en `/segments/execute-ids`) fuera de su contrato. Es un error de quien llama, no de la definición. - `UNAVAILABLE_CRITERION`: el criterio existe pero no está disponible en este entorno. - `INVALID_CRITERION_VALUE`: un valor de criterio no encaja con su tipo (número no representable, fecha que BigQuery no convierte, lista vacía donde se exige un valor…). Todos los valores de la DSL viajan como parámetros de BigQuery, nunca interpolados. - `INVALID_GROUPS`: `groups.total_groups` fuera de 1–100 o `group_number` fuera de rango. - `INVALID_AVERAGE_THRESHOLD`: umbral no válido de un criterio AVG monetario (CURRENCY), sea la media exacta o la media económica canónica (`average_spent` antes y después de migrar el catálogo). Se aplica después de `INVALID_CRITERION_VALUE`, que ya rechaza lo que no es un número finito cuyos céntimos sean un entero seguro (|céntimos| < 2^53); la media rechaza además el texto numérico que no es decimal (`"0x10"`, `"0b1"`) y un `between` que no sea un par `[mínimo, máximo]` (p. ej. `{start, end}`). `is_set` e `is_not_set` no llevan umbral.
- `403`: Acceso denegado antes de ejecutar ninguna consulta. Dos casos: - **Rol no admitido** (`error.code: FORBIDDEN`): las rutas de datos solo admiten tokens con rol ADMIN, OWNER o SUPERADMIN (los `campaign_service_token` de nev-api llevan rol ADMIN). Un token de fan (USER), STAFF, EXTERNAL o sin rol recibe 403. - **Selección ajena** (`code: ACTIVE_TENANT_FORBIDDEN`): la organización seleccionada (`activeTenantId`) no pertenece a la jerarquía actual de `tenantId`.
- `500`: Error interno del servidor
- `503`: No se pudo leer la jerarquía de tenants (MongoDB no disponible). Reintentable; nunca se sustituye por un ámbito parcial o global.

---

[API](https://skmtc.dev/nevent/apis/nevent-data-api.md) · [All operations](https://skmtc.dev/nevent/apis/nevent-data-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nevent/apis/nevent-data-api/revisions/81035d32c1c1?raw)
