# Estructura de la información

## Introducción

Para operar correctamente la reventa, menta tech necesita una representación estructurada de tus eventos. Esta guía detalla la jerarquía de datos y los modelos disponibles para representar tu inventario, independientemente del método de sincronización que utilices.

{% callout type="info" title="Dos métodos de sincronización: PUSH y PULL" %}
La **estructura de datos que definas en esta guía aplica a ambos métodos** de sincronización con menta tech:

- **PUSH** — Tu backend envía requests a la API de menta (`POST /v1/events`, `POST /v1/events/rules`) cada vez que hay cambios. Es el método recomendado para integraciones en tiempo cercano a real.
- **PULL** — Tu plataforma expone un endpoint público y menta lo consulta periódicamente. La información viaja dentro del mismo JSON de eventos (ver [Métodos de Sincronización](/es/guides/eventData)).

Lo que cambia entre ambos es el **transporte**, no los conceptos: `ticketOption`, `priceType` y `combinations` se definen igual en los dos. Más abajo, dentro de cada modelo (Estandar y Detalle de Precio), vas a encontrar la implementación concreta para cada método.
{% /callout %}

## Jerarquía de Datos

La estructura base conecta eventos, funciones y categorías de acceso.

{% columns gap="2rem" align="start" %}

{% column width="1" valign="center" %}
{% diagram type="erd" height="500px" title="Estructura de Datos" %}
{
  "nodes": [
    {
      "id": "node1",
      "type": "custom",
      "position": { "x": 88, "y": 86 },
      "data": {
        "label": "Evento",
        "subtitle": "id, title, producer",
        "background": "#ffffff"
      }
    },
    {
      "id": "node-shows",
      "type": "custom",
      "position": { "x": 84, "y": 238 },
      "data": {
        "label": "Shows",
        "subtitle": "showId, date, title",
        "background": "#fff3e0"
      }
    },
    {
      "id": "node-ticketA",
      "type": "custom",
      "position": { "x": -21, "y": 365 },
      "data": {
        "label": "ticketOption A",
        "subtitle": "Acceso General, id",
        "background": "#e3f2fd"
      }
    },
    {
      "id": "node-ticketB",
      "type": "custom",
      "position": { "x": 198, "y": 364 },
      "data": {
        "label": "ticketOption B",
        "subtitle": "Acceso VIP, id",
        "background": "#e3f2fd"
      }
    }
  ],
  "edges": [
    {
      "id": "edge-1",
      "source": "node1",
      "target": "node-shows",
      "label": "tiene uno o más",
      "animated": false
    },
    {
      "id": "edge-2",
      "source": "node-shows",
      "target": "node-ticketA",
      "animated": true
    },
    {
      "id": "edge-3",
      "source": "node-shows",
      "target": "node-ticketB",
      "animated": true
    }
  ]
}
{% /diagram %}
{% /column %}

{% column width="1" valign="center" %}

Esta jerarquía permite:
- **Validar tickets**: Confirmar elegibilidad según evento y show.
- **Generar la UI**: Mostrar categorías y mapas correctamente.
- **Aplicar reglas**: Configurar reventa a nivel evento, show o categoría.

El punto crítico es cómo defines las **categorías de acceso** (ticketOptions) y sus precios.

{% /column %}
{% /columns %}

## Modelado de Precios e Inventario

Dos dimensiones definen la estructura de tu inventario:

- **ticketOption**: la categoría de acceso (Campo, VIP). Es lo que el comprador de reventa **ve y elige**. Lleva su propio `price` (precio de la categoría **excluyendo comisiones y cargos**) y opcionalmente un `area` (agrupador de zona — varios ticketOptions pueden compartir el mismo `area`, ej: `Platea Alta VIP` y `Platea Alta Regular` con `"area": "Platea Alta"`).
- **priceType**: la variación comercial (Preventa, Early Bird). Es metadata interna.
- **combination** (cuando existen priceTypes): el par `ticketOption` + `priceType` que realmente se vende. Lleva su propio `price` (también **sin comisiones ni cargos**) y, opcionalmente, `description`. **El precio de la combinación prevalece** sobre el de la ticketOption asociada.

### ¿Por qué separarlos?

Si mezclas ambos conceptos (ej: "Campo Preventa" y "Campo General" como categorías distintas), fragmentas el inventario y confundes al comprador. Al separarlos, agrupas todo el inventario de "Campo" junto, independientemente del precio original.

menta ofrece dos modelos de integración para manejar esto:

## Tabla comparativa de modelos

{% table highlight-first=true %}
| Modelo | Qué envías | Cómo interpreta los datos menta | Cuándo tiene sentido | Beneficios | Limitaciones |
|--------|------------|--------------------------------|----------------------|------------|--------------|
| Estandar | Solo ticketOptions | Una categoría unificada por tipo de acceso | Cuando no existen variaciones de precio dentro del acceso | Integración más simple, mínima configuración | No distingue diferencias de precio, agrupamiento y filtros limitados |
| Detalle de Precio | ticketOptions + priceTypes + combinaciones reales | Solo existen las combinaciones creadas explícitamente | Cuando se necesita máximo control y precisión | El dashboard refleja exactamente tu catálogo, ideal para pricing avanzado | Requiere mantener combinaciones vía API |
{% /table %}

{% conditionaltabs id="tabs-1765310713135" %}
{% tab label="Estandar" %}
## Estandar

{% columns gap="2rem" align="start" %}
{% column width="1" %}
### Descripción general

En el modelo Estandar tu plataforma envía únicamente las ticketOptions. menta asume que cada ticketOption representa un producto único sin variaciones internas de precio.

### Implementación

Enviás únicamente las `ticketOptions`. No hace falta declarar `priceTypes` ni combinaciones. El transporte cambia según el método de sincronización que uses.
{% /column %}

{% column width="1" valign="center" %}
{% diagram type="erd" height="200px" title="Flujo Estandar" %}
{
  "nodes": [
    { "id": "in", "type": "custom", "position": { "x": 20, "y": 75 }, "data": { "label": "TicketOption", "subtitle": "Campo", "background": "#e3f2fd" } },
    { "id": "out", "type": "custom", "position": { "x": 400, "y": 75 }, "data": { "label": "Producto Final", "subtitle": "Campo", "background": "#e8f5e9" } }
  ],
  "edges": [
    { "id": "e1", "source": "in", "target": "out", "label": "se convierte en", "animated": true, "style": { "stroke": "#4caf50" } }
  ]
}
{% /diagram %}
{% /column %}
{% /columns %}

#### Opción A — PUSH (API de menta)

Un único API call a `POST /v1/events` enviando solo las `ticketOptions` dentro de cada show.

{% apiembed endpoint="post-events" show="link" width="auto" /%}

{% columns gap="2rem" align="center" %}

{% column width="0.5" valign="center" %}
{% callout type="info" title="Estructura del request" %}
En el modelo Estandar, solo envías las ticketOptions sin información de priceTypes. menta tech tratará cada ticketOption como una categoría única sin variaciones de precio.
{% /callout %}

<br>

```json
{
  "externalReferenceId": "event-123",
  "shows": {
    "showId": "show-456",
    "ticketOptions": [
      {
        "title": "Campo General",
        "ticketId": "campo-general",
        "description": "Acceso general al campo",
        "currency": "USD",
        "price": 100,
        "area": "Campo"
      },
      {
        "title": "VIP",
        "ticketId": "vip",
        "description": "Acceso VIP con beneficios exclusivos",
        "currency": "USD",
        "price": 250,
        "area": "Platea"
      }
    ]
  }
}
```
{% /column %}

{% column width="0.5" valign="center" sticky=true %}

{% codeannotation highlight-lines="2" %}
**externalReferenceId**: Identificador único del evento en tu plataforma. Se usa para todas las referencias futuras.
{% /codeannotation %}

{% codeannotation highlight-lines="4" %}
**showId**: Identificador único del show en tu sistema.
{% /codeannotation %}

{% codeannotation highlight-lines="5" %}
**ticketOptions**: Array con las categorías de acceso disponibles. Solo envías las ticketOptions, sin priceTypes.
{% /codeannotation %}

{% codeannotation highlight-lines="7" %}
**title**: Nombre visible de la categoría para el comprador de reventa.
{% /codeannotation %}

{% codeannotation highlight-lines="8" %}
**ticketId**: Identificador único de la categoría de acceso en tu sistema (equivalente a ticketOptionId).
{% /codeannotation %}

{% codeannotation highlight-lines="9" %}
**description**: Descripción de la categoría de acceso.
{% /codeannotation %}

{% codeannotation highlight-lines="10" %}
**currency**: Moneda del precio del ticket.
{% /codeannotation %}

{% codeannotation highlight-lines="11" %}
**price**: Precio de la categoría **excluyendo comisiones y cargos**.
{% /codeannotation %}

{% codeannotation highlight-lines="12" %}
**area** *(opcional)*: Agrupador de zona. Varios ticketOptions pueden compartir el mismo valor (ej: `Platea Alta VIP` y `Platea Alta Regular` con `"area": "Platea Alta"`).
{% /codeannotation %}

{% /column %}
{% /columns %}

#### Opción B — PULL (Endpoint expuesto)

En el JSON que devuelve tu endpoint (ej: `https://api.tuplataforma.com/menta-feed`), dentro de cada show incluís solo el array `ticketOptions`. No agregues `priceTypes` ni `combinations`: al omitirlos, menta interpreta el show bajo el modelo Estandar.

Consultá la guía de **[Métodos de Sincronización](/es/guides/eventData)** para ver la estructura completa del endpoint (location, producers, shows, etc.).

```json
{
  "externalReferenceId": "event-123",
  "shows": [
    {
      "showId": "show-456",
      "status": "ON_SALE",
      "dates": { "startsAt": "2025-12-15T20:00:00.000-03:00" },
      "ticketOptions": [
        {
          "title": "Campo General",
          "ticketId": "campo-general",
          "description": "Acceso general al campo",
          "currency": "USD",
          "price": 100,
          "area": "Campo"
        },
        {
          "title": "VIP",
          "ticketId": "vip",
          "description": "Acceso VIP con beneficios exclusivos",
          "currency": "USD",
          "price": 250,
          "area": "Platea"
        }
      ]
    }
  ]
}
```

### Cuándo tiene sentido este modelo

- Tus eventos no utilizan múltiples categorías de precio dentro de un mismo tipo de acceso
- Buscas la integración más simple posible
- No necesitas agrupamiento, filtrado o alertas basadas en precio

### Beneficios

- Menor esfuerzo de implementación
- Configuración de reglas muy simple, una regla por ticketOption
- No es necesario gestionar priceTypes ni combinaciones

### Limitaciones

- Si existen múltiples precios internos, el comprador no verá agrupaciones significativas
- Todos los tickets del mismo acceso se ven idénticos en reventa aunque hayan sido comprados en condiciones distintas
- No hay soporte para alertas o filtros más allá de la propia ticketOption
{% /tab %}
{% tab label="Detalle de Precio" %}
## Detalle de Precio

{% columns gap="2rem" align="start" %}
{% column width="1" %}
### Descripción general

En el modelo de Detalle de Precio Avanzado, tu plataforma envía las `ticketOptions` y `priceTypes`, y además defines explícitamente solo las combinaciones reales que existen en tu catálogo usando el endpoint `POST /v1/events/rules` con `ruleType: COMBINATION`.

menta tech **no genera combinaciones automáticamente**. Solo existirán las combinaciones que definas explícitamente.

### Implementación

Además de las `ticketOptions`, declarás los `priceTypes` disponibles y las **combinaciones reales** que existen en tu catálogo. El transporte cambia según el método de sincronización que uses.
{% /column %}

{% column width="1" valign="center" %}
{% diagram type="erd" height="350px" title="Combinación Explícita" %}
{
  "nodes": [
    {
      "id": "to",
      "type": "custom",
      "position": {
        "x": 692.3844155844157,
        "y": 94.04220779220779
      },
      "data": {
        "label": "TicketOption",
        "subtitle": "VIP",
        "background": "#e3f2fd"
      }
    },
    {
      "id": "pt1",
      "type": "custom",
      "position": {
        "x": 960.9032467532467,
        "y": 94.04220779220779
      },
      "data": {
        "label": "PriceType",
        "subtitle": "Preventa",
        "background": "#f3e5f5"
      }
    },
    {
      "id": "pt2",
      "type": "custom",
      "position": {
        "x": 414.7551948051948,
        "y": 94.04220779220779
      },
      "data": {
        "label": "PriceType",
        "subtitle": "General",
        "background": "#f3e5f5"
      }
    },
    {
      "id": "blocked",
      "type": "custom",
      "position": {
        "x": 808.6194805194805,
        "y": 250
      },
      "data": {
        "label": "No Generado",
        "subtitle": "Sin regla explícita",
        "background": "#ffebee"
      }
    },
    {
      "id": "res2",
      "type": "custom",
      "position": {
        "x": 578.8194805194805,
        "y": 250
      },
      "data": {
        "label": "Producto",
        "subtitle": "VIP + General",
        "background": "#e8f5e9"
      }
    }
  ],
  "edges": [
    {
      "id": "e_b1",
      "source": "to",
      "target": "blocked",
      "animated": false,
      "style": {
        "stroke": "#e57373",
        "strokeDasharray": "5,5"
      },
      "markerEnd": {
        "type": "arrowclosed",
        "color": "#9ca3af",
        "width": 16,
        "height": 16
      }
    },
    {
      "id": "e_b2",
      "source": "pt1",
      "target": "blocked",
      "animated": false,
      "style": {
        "stroke": "#e57373",
        "strokeDasharray": "5,5"
      },
      "markerEnd": {
        "type": "arrowclosed",
        "color": "#9ca3af",
        "width": 16,
        "height": 16
      }
    },
    {
      "id": "e2",
      "source": "to",
      "target": "res2",
      "animated": true,
      "markerEnd": {
        "type": "arrowclosed",
        "color": "#9ca3af",
        "width": 16,
        "height": 16
      }
    },
    {
      "id": "e4",
      "source": "pt2",
      "target": "res2",
      "animated": true,
      "style": {
        "stroke": "#9c27b0"
      },
      "markerEnd": {
        "type": "arrowclosed",
        "color": "#9ca3af",
        "width": 16,
        "height": 16
      }
    }
  ]
}
{% /diagram %}
{% /column %}
{% /columns %}

#### Opción A — PUSH (API de menta)

Tu plataforma realiza **dos API calls secuenciales**:

1. **Paso 1** — Crear el evento con `ticketOptions` y `priceTypes` usando `POST /v1/events`.
2. **Paso 2** — Definir las combinaciones reales usando `POST /v1/events/rules` con `ruleType: COMBINATION`.

##### Paso 1: Crear el evento

Primero, creas el evento enviando las dimensiones base (ticketOptions y priceTypes):

{% apiembed endpoint="post-events" show="link" width="auto" /%}

{% columns gap="2rem" align="center" %}

{% column width="0.5" valign="center" %}
{% callout type="info" title="Estructura del request" %}
Envías las ticketOptions y priceTypes disponibles en tu sistema. En este punto, menta tech aún no crea combinaciones.
{% /callout %}

<br>

```json
{
  "externalReferenceId": "event-123",
  "shows": {
    "showId": "show-456",
    "ticketOptions": [
      {
        "title": "Campo General",
        "ticketId": "campo-general",
        "description": "Acceso general al campo",
        "currency": "USD",
        "price": 100,
        "area": "Campo"
      },
      {
        "title": "VIP",
        "ticketId": "vip",
        "description": "Acceso VIP con beneficios exclusivos",
        "currency": "USD",
        "price": 250,
        "area": "Platea"
      }
    ],
    "priceTypes": [
      {
        "priceTypeId": "preventa",
        "title": "Preventa"
      },
      {
        "priceTypeId": "regular",
        "title": "Precio Regular"
      }
    ]
  }
}
```
{% /column %}

{% column width="0.5" valign="center" sticky=true %}

{% codeannotation highlight-lines="2" %}
**externalReferenceId**: Identificador único del evento en tu plataforma. Se usará en el siguiente paso.
{% /codeannotation %}

{% codeannotation highlight-lines="4" %}
**showId**: Identificador único del show en tu sistema. Se usará en el siguiente paso.
{% /codeannotation %}

{% codeannotation highlight-lines="5" %}
**ticketOptions**: Array con las categorías de acceso disponibles en tu catálogo.
{% /codeannotation %}

{% codeannotation highlight-lines="7" %}
**title**: Nombre visible de la categoría para el comprador de reventa.
{% /codeannotation %}

{% codeannotation highlight-lines="8" %}
**ticketId**: Identificador único de la categoría de acceso en tu sistema (equivalente a ticketOptionId).
{% /codeannotation %}

{% codeannotation highlight-lines="11" %}
**price**: Precio de la categoría **excluyendo comisiones y cargos**. Si declarás `combinations`, el precio de cada combinación prevalece sobre éste.
{% /codeannotation %}

{% codeannotation highlight-lines="12" %}
**area** *(opcional)*: Agrupador de zona. Varios ticketOptions pueden compartir el mismo valor.
{% /codeannotation %}

{% codeannotation highlight-lines="23" %}
**priceTypes**: Array con los tipos de precio disponibles en tu sistema.
{% /codeannotation %}

{% codeannotation highlight-lines="25" %}
**priceTypeId**: Identificador único del tipo de precio en tu sistema. Se usará en el siguiente paso.
{% /codeannotation %}

{% /column %}
{% /columns %}

##### Paso 2: Definir combinaciones reales

Luego, defines explícitamente solo las combinaciones que realmente existen en tu catálogo:

{% apiembed endpoint="post-events-rules" show="link" width="auto" /%}

{% columns gap="2rem" align="center" %}

{% column width="0.5" valign="center" %}
{% callout type="info" title="Estructura del request" %}
Solo defines las combinaciones que realmente existen en tu sistema. menta tech no generará combinaciones automáticamente. Debes especificar cada combinación válida usando `ruleType: COMBINATION`.
{% /callout %}

<br>

```json
[
  {
    "externalReferenceEventId": "event-123",
    "showId": "show-456",
    "ticketOptionId": "campo-general",
    "priceTypeId": "preventa",
    "ruleType": "COMBINATION",
    "price": 80,
    "description": "Campo General – Preventa"
  },
  {
    "externalReferenceEventId": "event-123",
    "showId": "show-456",
    "ticketOptionId": "campo-general",
    "priceTypeId": "regular",
    "ruleType": "COMBINATION",
    "price": 100
  },
  {
    "externalReferenceEventId": "event-123",
    "showId": "show-456",
    "ticketOptionId": "vip",
    "priceTypeId": "preventa",
    "ruleType": "COMBINATION",
    "price": 200
  }
]
```
{% /column %}

{% column width="0.5" valign="center" sticky=true %}

{% codeannotation highlight-lines="2" %}
El body es un array de objetos, cada uno representa una combinación válida.
{% /codeannotation %}

{% codeannotation highlight-lines="4" %}
**externalReferenceEventId**: El identificador único del evento (debe coincidir con el enviado en el Paso 1).
{% /codeannotation %}

{% codeannotation highlight-lines="5" %}
**showId**: El identificador único del show (debe coincidir con el enviado en el Paso 1).
{% /codeannotation %}

{% codeannotation highlight-lines="6" %}
**ticketOptionId**: Identificador de la categoría de acceso de esta combinación (debe coincidir con el `ticketId` enviado en el Paso 1).
{% /codeannotation %}

{% codeannotation highlight-lines="7" %}
**priceTypeId**: Identificador del tipo de precio de esta combinación (debe coincidir con el enviado en el Paso 1).
{% /codeannotation %}

{% codeannotation highlight-lines="8" %}
**ruleType**: Debe ser `"COMBINATION"` para definir combinaciones válidas entre ticketOption y priceType.
{% /codeannotation %}

{% codeannotation highlight-lines="9" %}
**price**: Precio de la combinación **excluyendo comisiones y cargos**. Prevalece sobre el `price` declarado en la ticketOption asociada.
{% /codeannotation %}

{% codeannotation highlight-lines="10" %}
**description** *(opcional)*: Texto libre que describe la combinación (por ejemplo, una etiqueta comercial o interna).
{% /codeannotation %}

{% /column %}
{% /columns %}

**Nota importante**: Solo las combinaciones que definas explícitamente en este segundo API call aparecerán en el dashboard y en la matriz de reventa. Si no defines una combinación (por ejemplo, VIP con Precio Regular), esa combinación no existirá en menta aunque ambas dimensiones estén sincronizadas en el Paso 1.

#### Opción B — PULL (Endpoint expuesto)

En el JSON que devuelve tu endpoint, dentro de cada show incluís tres arrays: `ticketOptions`, `priceTypes` y `combinations`. **Todo viaja en un único payload**, no hay dos pasos como en PUSH.

{% callout type="warning" title="Regla clave del PULL" %}
Si declarás `priceTypes`, **es obligatorio** incluir `combinations` listando todos los pares `ticketOption` + `priceType` válidos. menta tech no genera combinaciones automáticamente; solo existirán las que aparezcan en ese array.

Si tu catálogo no tiene variaciones de precio, omití `priceTypes` y `combinations` y usá el modelo Estandar.
{% /callout %}

{% columns gap="2rem" align="center" %}

{% column width="0.5" valign="center" %}
```json
{
  "externalReferenceId": "event-123",
  "shows": [
    {
      "showId": "show-456",
      "status": "ON_SALE",
      "dates": { "startsAt": "2025-12-15T20:00:00.000-03:00" },
      "ticketOptions": [
        {
          "title": "Campo General",
          "ticketId": "campo-general",
          "description": "Acceso general al campo",
          "currency": "USD",
          "price": 100,
          "area": "Campo"
        },
        {
          "title": "VIP",
          "ticketId": "vip",
          "description": "Acceso VIP con beneficios exclusivos",
          "currency": "USD",
          "price": 250,
          "area": "Platea"
        }
      ],
      "priceTypes": [
        {
          "priceTypeId": "preventa",
          "title": "Preventa"
        },
        {
          "priceTypeId": "regular",
          "title": "Precio Regular"
        }
      ],
      "combinations": [
        {
          "ticketOptionId": "campo-general",
          "priceTypeId": "preventa",
          "price": 80,
          "description": "Campo General – Preventa"
        },
        {
          "ticketOptionId": "campo-general",
          "priceTypeId": "regular",
          "price": 100
        },
        {
          "ticketOptionId": "vip",
          "priceTypeId": "preventa",
          "price": 200
        }
      ]
    }
  ]
}
```
{% /column %}

{% column width="0.5" valign="center" sticky=true %}

{% codeannotation highlight-lines="7" %}
**ticketOptions**: Categorías de acceso visibles para el comprador. Cada una declara su `price` (sin comisiones ni cargos) y opcionalmente un `area`.
{% /codeannotation %}

{% codeannotation highlight-lines="14" %}
**price** (ticketOption): Precio base de la categoría, **excluyendo comisiones y cargos**. Cuando hay `combinations`, el precio de la combinación prevalece sobre éste.
{% /codeannotation %}

{% codeannotation highlight-lines="15" %}
**area** *(opcional)*: Agrupador de zona. Varios ticketOptions pueden compartir el mismo valor (ej: `Platea Alta VIP` y `Platea Alta Regular` con `"area": "Platea Alta"`).
{% /codeannotation %}

{% codeannotation highlight-lines="25" %}
**priceTypes**: Tipos de precio disponibles para la venta (ej: Adult, Special Discount, Preventa). Es **opcional**: si no tenés variaciones internas de precio, omití este array.
{% /codeannotation %}

{% codeannotation highlight-lines="35" %}
**combinations**: Pares `ticketOption` + `priceType` que realmente existen en tu catálogo. Si enviás `priceTypes`, este array **es obligatorio**. La combinación `vip + regular` no aparece porque no existe en el catálogo, y por lo tanto no se generará en menta.
{% /codeannotation %}

{% codeannotation highlight-lines="39" %}
**price** (combination): Precio de la combinación específica, **sin comisiones ni cargos**.
{% /codeannotation %}

{% codeannotation highlight-lines="40" %}
**description** *(opcional)*: Texto libre que describe la combinación.
{% /codeannotation %}

{% /column %}
{% /columns %}

Consultá la guía de **[Métodos de Sincronización](/es/guides/eventData)** para ver la estructura completa del endpoint (location, producers, dates, etc.).

### Cuándo tiene sentido este modelo

- Necesitas un mapeo preciso entre menta y tu catálogo real
- No todas las ticketOptions se combinan con todos los priceTypes
- Quieres evitar mostrar categorías o combinaciones que no existen en tu sistema primario

### Beneficios

- El dashboard refleja exactamente la configuración real de tus eventos
- No aparecen combinaciones artificiales o sin uso
- Ideal para estrategias comerciales complejas y eventos de gran escala

### Limitaciones

- Requiere gestionar combinaciones vía API
- Debe actualizarse cada vez que cambian las ticketOptions o los priceTypes
{% /tab %}
{% /conditionaltabs %}
