---
title: "Ids de los fans de una ejecución, por páginas de hasta 25000"
method: POST
path: "/segments/execute-ids"
tags: ["Segmentation"]
---

# Ids de los fans de una ejecución, por páginas de hasta 25000

`POST /segments/execute-ids`

## Request

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

Devuelve solo los `fan_id` que devolvería `/segments/execute` para la misma petición: misma
`definition`, mismos tenants (los del JWT, nunca del cuerpo), mismos `groups` y mismo
`definition.limit`/`offset`. Sin datos de contacto ni enriquecimiento de consentimiento.

**Paginación por clave** (`after_user_id`): ids distintos y no vacíos en orden ascendente;
la siguiente página se pide con `after_user_id` = `next_after_user_id` de la anterior.
Cada página es una consulta nueva sobre datos vivos, no una instantánea: un fan que entra o
sale entre dos páginas no desplaza a los demás, pero puede aparecer o no según el momento.
`page` no se admite (400 `INVALID_PAGINATION`).

- `page_size`: 1–25000 (default 100).
- `has_more`: hay otra página; `next_after_user_id` es `null` cuando no la hay.

**Rate limit**: comparte el límite de `/segments/execute` (30 req/min por tenant): las
páginas de ambos endpoints gastan el mismo presupuesto.

## Request body

- object
  - `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.
  - `page_size` integer
  - `after_user_id` string, nullable: Último id de la página anterior; ausente o null para la primera.
  - `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)

## Response `200`

Página de ids

- object
  - `success` boolean
  - `fan_ids` string[]
  - `page_size` integer
  - `has_more` boolean
  - `next_after_user_id` string, nullable

## 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)
