# Métodos de Sincronización

## Introducción

La sincronización de eventos permite a menta tech conocer tu inventario para habilitar la reventa. Existen diferentes métodos para lograr esta sincronización, dependiendo de las capacidades técnicas de tu plataforma.

Independientemente del método elegido, la estructura de la información (cómo defines tickets y precios) es crítica. Consulta la guía de **[Estructura de Datos](/es/guides/infoStructure)** para entender cómo modelar tu inventario correctamente.

## Opciones de Integración

{% table highlight-first=true highlight-row=3 %}
| Opción | Descripción | Cuándo tiene sentido | Beneficios | Limitaciones |
|--------|-------------|----------------------|------------|--------------|
| Usando la API de menta tech | La plataforma envía peticiones de CREATE/UPDATE directamente a menta | Cuando la sincronización en tiempo real y la precisión son importantes | Actualizaciones en tiempo cercano a real, sin polling, opción más escalable | Requiere emitir actualizaciones salientes desde el backend |
| Endpoint de eventos expuesto por la plataforma (recomendado) | menta tech consulta periódicamente un endpoint de eventos de la plataforma de ticketing | Cuando ya existe un endpoint de eventos ó se prefiere un modelo basado en consultas (pull) | Adopción sencilla, la plataforma controla estructura y frecuencia, menta se encarga de la sincronización | Los datos pueden quedar desactualizados, el polling suma carga y no es ideal para cambios frecuentes |
| Carga manual de eventos | Los eventos se crean de forma manual, sin usar APIs | Para pilotos o partners de bajo volumen | Sin desarrollo, salida a producción más rápida | No escalable, operación manual, mayor riesgo de datos desactualizados |
{% /table %}

{% conditionaltabs id="tabs-1765309889524" %}

{% tab label="API de menta tech" %}
## Envío vía API (Push)

Usa este método cuando tu backend puede emitir actualizaciones en tiempo real. Tu backend actúa como fuente de verdad y notifica a menta cada vez que hay cambios.

### Implementación

Debes enviar requests a nuestra API cada vez que se cree, actualice o cancele un evento.

```bash
POST /v1/events
```

El cuerpo del request (JSON) dependerá del modelo de datos que elijas (Estandar, Precio Simple o Avanzado). **Revisa la guía de [Estructura de Datos](/es/guides/infoStructure)** para ver los esquemas JSON requeridos.

Enlaces útiles:
- <a href='https://connect.mentatech.io/es/api/post-events' target="_blank">Crear evento (API Reference)</a>
- <a href='https://connect.mentatech.io/es/api/put-events' target="_blank">Actualizar evento (API Reference)</a>
{% /tab %}

{% tab label="Endpoint expuesto por tu plataforma" %}
## Endpoint Pull

Este es el método de integración recomendado: tu plataforma expone una URL pública (ej: `https://api.tuplataforma.com/menta-feed`) y menta tech la consulta periódicamente para buscar actualizaciones — tú controlas el endpoint y menta se encarga de la sincronización.

### Requisitos del Endpoint

El endpoint debe devolver un JSON con la lista de eventos activos (un array de objetos). Cada evento lleva su show con el venue (`location`), imágenes, fechas del ciclo de vida y la estructura de tickets expresada como **combinations**.

### Estructura JSON requerida

A continuación, un ejemplo de cómo debe verse el array de eventos devuelto por tu endpoint:

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

{% column width="1" valign="top" %}
**Campos clave:**
- **Array Raíz**: La respuesta debe ser una lista `[...]` con una entrada por evento activo.
- **Evento**: `title`, `description`, `externalReferenceId` (tu id estable del evento — nunca se reutiliza ni se muta) y, opcionalmente, `producers` (información de contacto).
- **Show** (`shows`): `title`, `showId` (tu id estable del show), `status` (el estado nativo de tu plataforma — ej: `ON_SALE`, `CANCELLED`), `dates` (ISO 8601 con timezone: `startsAt`, `salesStartAt`, `salesEndAt`, `publicAt`), `location` (`address`, `city`, `state`, `postalCode`, `country` y, opcionalmente, `venueName`), `images` (al menos una imagen `main`), y opcionalmente `primarySalesUrl` y `tags` (strings `"key:value"`).
- **combinations**: una entrada por cada combinación existente de valores de data fields en tu admin — cada hoja del árbol de tu estructura de tickets. Cada combinación lleva:
  - `values`: una entrada `{ valueId, fieldTitle, value }` por data field. `fieldTitle` es el label del campo tal como está tipeado en tu admin, `value` es la opción tal como está tipeada, y `valueId` es tu id estable de ese valor (persiste ante renombres).
  - `price` (valor facial, **excluyendo comisiones y cargos**) y `currency` (ISO 4217).
  - `stock`: `{ total, remaining }`, o `null` cuando la combinación no tiene cupo fijo.
  - `webVisibility` (envía `false` cuando la combinación está pausada u oculta) y `webSoldOut` (envía `true` cuando está agotada).
  - `attendeeQuestions` *(opcional)*: preguntas de checkout por ticket para esta combinación (ver abajo).
- **buyerQuestions** *(opcional, a nivel show)*: preguntas de checkout por orden (ver abajo).
{% /column %}

{% column width="1" valign="top" %}
```json
[
  {
    "title": "Concierto Ejemplo",
    "description": "Descripción del evento de ejemplo.",
    "externalReferenceId": "evt_example_001",
    "producers": [
      {
        "name": "Productor Ejemplo",
        "emails": ["contacto@productor-ejemplo.com"],
        "producerId": "prod_001"
      }
    ],
    "shows": {
      "title": "Concierto Ejemplo - Función Principal",
      "dates": {
        "startsAt": "2025-12-15T20:00:00.000-03:00",
        "salesStartAt": "2025-10-01T10:00:00.000-03:00",
        "salesEndAt": "2025-12-15T18:00:00.000-03:00",
        "publicAt": "2025-09-15T00:00:00.000-03:00"
      },
      "showId": "show_example_001",
      "status": "ON_SALE",
      "location": {
        "address": "Av. Ejemplo 1234",
        "city": "Buenos Aires",
        "state": "Buenos Aires",
        "postalCode": "1425",
        "country": "Argentina",
        "venueName": "Estadio Ejemplo"
      },
      "images": [
        { "url": "https://ejemplo.com/imagenes/evento-principal.jpg", "kind": "main" }
      ],
      "primarySalesUrl": "https://tuplataforma.com/evento/concierto-ejemplo/show/001",
      "tags": ["genre:rock", "tour:2025"],
      "combinations": [
        {
          "values": [
            { "valueId": "v_cat_general", "fieldTitle": "Categoría", "value": "General" },
            { "valueId": "v_tar_adult", "fieldTitle": "Tarifa", "value": "Adulto" }
          ],
          "price": 100,
          "currency": "USD",
          "stock": { "total": 500, "remaining": 380 },
          "webVisibility": true,
          "webSoldOut": false
        },
        {
          "values": [
            { "valueId": "v_cat_general", "fieldTitle": "Categoría", "value": "General" },
            { "valueId": "v_tar_special", "fieldTitle": "Tarifa", "value": "Descuento especial" }
          ],
          "price": 70,
          "currency": "USD",
          "stock": { "total": 500, "remaining": 470 },
          "webVisibility": true,
          "webSoldOut": false
        },
        {
          "values": [
            { "valueId": "v_cat_vip", "fieldTitle": "Categoría", "value": "VIP" },
            { "valueId": "v_tar_adult", "fieldTitle": "Tarifa", "value": "Adulto" }
          ],
          "price": 250,
          "currency": "USD",
          "stock": null,
          "webVisibility": true,
          "webSoldOut": false
        }
      ]
    }
  }
]
```
{% /column %}
{% /columns %}

{% callout type="warning" title="Las combinations son la fuente de verdad" %}
menta no deriva ni inventa combinaciones: **solo existen las combinaciones que envías**. Envía una entrada por cada hoja vendible de tu estructura de tickets, sin importar cómo se llamen tus data fields. Cada combinación se identifica de forma única por el conjunto de `valueId`s que lleva — no hace falta un id de combinación aparte — por lo que `externalReferenceId`, `showId` y cada `valueId` deben ser **ids estables** que persistan ante renombres y nunca se reutilicen.
{% /callout %}

{% callout type="info" title="Precios sin comisiones ni cargos" %}
`combination.price` es el **valor facial**: el precio de esa combinación tal como lo cobrás vos, sin fees de servicio ni impuestos adicionales que puedas sumar en el checkout primario.
{% /callout %}

### Preguntas al comprador y al asistente

Si tu checkout recolecta información custom (fecha de nacimiento, número de carnet de estudiante, talle de remera), envía las definiciones de las preguntas en el payload y menta recolecta las mismas respuestas del comprador de reventa y te las devuelve en el webhook de cambio de titularidad:

- **`buyerQuestions`** *(a nivel show)*: se preguntan una vez por orden. No varían por categoría.
- **`attendeeQuestions`** *(dentro de cada combinación)*: se preguntan una vez por ticket, así pueden diferir por categoría. Cuando la misma pregunta aplica a varias combinaciones, se repite en cada una manteniendo el mismo `questionId`.

Una pregunta de entrada libre, opcionalmente restringida vía `validator` (`{ regex }` para text/textarea/email/number, `{ min, max }` para date/time/number):

```json
{ "questionId": "q_birthdate", "title": "Fecha de nacimiento", "type": "date", "required": true,
  "validator": { "min": "1900-01-01", "max": "2008-06-29" } }
```

Una pregunta con opciones (`single_select` = elige una, `multi_select` = elige varias; `optionId` es estable, `value` es el label que se muestra):

```json
{ "questionId": "q_tshirt", "title": "Talle de remera", "type": "single_select", "required": true,
  "options": [ { "optionId": "s", "value": "S" }, { "optionId": "m", "value": "M" }, { "optionId": "l", "value": "L" } ] }
```

`type` es uno de `text`, `textarea`, `email`, `number`, `date`, `time` (entrada libre) o `single_select` / `multi_select` (con opciones). `questionId` es tu id estable — persiste ante renombres y es la clave que menta usa para mapear cada respuesta a su pregunta. Cuando `required` es `true`, menta bloquea el checkout hasta que el comprador responda. menta renderiza **solo** las preguntas que envías: omite cualquier pregunta que no deba mostrarse al comprador de reventa.
{% /tab %}

{% tab label="Carga manual de eventos" %}
## Carga manual

Para volúmenes bajos o pruebas piloto, es posible cargar eventos mediante archivos (CSV/Excel) procesados por el equipo de operaciones de menta.

Este método no requiere desarrollo pero tiene limitaciones operativas importantes y riesgo de desactualización de datos. Contacta a tu account manager si deseas utilizar esta opción.
{% /tab %}
{% /conditionaltabs %}

---

## Estado del Show

Cada show incluye un campo `status` que representa su estado actual en el ciclo de vida. Este estado determina cómo se muestra el show y si la actividad de reventa está habilitada.

{% table highlight-first=true %}
| Estado | Descripción | Visible al público | Reventa habilitada |
| :--- | :--- | :--- | :--- |
| `ON_SALE` | Los boletos se están vendiendo activamente en el mercado primario. El show es visible públicamente y la reventa está completamente operativa. | Sí | Sí |
| `ANNOUNCED` | El show ha sido anunciado públicamente y es visible, pero la venta primaria de boletos aún no ha comenzado o fue pausada. Los usuarios pueden ver el evento pero no pueden comprar boletos en el mercado primario. La reventa puede seguir activa si los boletos fueron distribuidos previamente. | Sí | Depende de la configuración |
| `CANCELLED` | El show ha sido cancelado. No se permite más actividad de venta ni reventa. Las publicaciones de reventa existentes se eliminan y las transacciones pendientes se manejan según la política de cancelación. | Sí (marcado como cancelado) | No |
| `RESCHEDULED` | El show fue movido a una nueva fecha u horario. El campo `dates.startsAt` refleja la nueva programación. La actividad de reventa puede continuar bajo la nueva fecha dependiendo de la configuración. | Sí (marcado como reprogramado) | Depende de la configuración |
| `HIDDEN` | El show no es visible al público. No aparece en resultados de búsqueda, listados de eventos ni en el marketplace de reventa. Se usa típicamente para shows en preparación o temporalmente removidos. | No | No |
| `PRIVATE` | El show está restringido a una audiencia específica (ej., solo invitados, grupos de preventa o eventos internos). No es visible en listados públicos pero puede ser accesible mediante link directo o código de acceso. | Solo con acceso directo | Depende de la configuración |
{% /table %}

{% callout type="info" title="Estado y fechas" %}
El campo `status` refleja el **estado operativo actual** del show, mientras que el objeto `dates` proporciona la **línea de tiempo programada**. Por ejemplo, un show puede tener `status: "ANNOUNCED"` con un `dates.salesStartAt` en el futuro — lo que significa que pasará automáticamente a estar en venta en esa fecha. Envía siempre ambos campos para que menta tech pueda gestionar el ciclo de vida del show correctamente.
{% /callout %}

## Fechas del Show

El objeto `dates` agrupa todas las fechas relevantes del ciclo de vida de un show. Todas las fechas deben estar en formato ISO 8601.

{% table highlight-first=true %}
| Campo | Descripción |
| :--- | :--- |
| `dates.startsAt` | Fecha y hora de inicio del show (el horario de la función). Es la fecha de referencia principal del evento. |
| `dates.salesStartAt` | Fecha y hora en que abre la venta primaria de boletos. Antes de esta fecha, los boletos no pueden comprarse en el mercado primario. |
| `dates.salesEndAt` | Fecha y hora en que cierra la venta primaria de boletos. Después de esta fecha, los boletos ya no están disponibles en el mercado primario. |
| `dates.publicAt` | Fecha y hora en que el show se hace visible públicamente en la web. Antes de esta fecha, el show puede existir internamente pero no se muestra a los usuarios finales. |
{% /table %}

---

## Cancelación de Eventos

{% callout type="warning" title="Importancia crítica" %}
**Es fundamental informar a menta tech cuando un evento es cancelado.** La distribución del dinero a los vendedores de boletos de reventa se realiza unos días después de la fecha original del evento. Si un evento se cancela y no se notifica a menta, los pagos se procesarán normalmente después de la fecha programada y **no habrá forma de recuperar el dinero** una vez distribuido.
{% /callout %}

### ¿Por qué es tan importante?

Cuando un evento se cancela:
1. **Los compradores de reventa deben ser reembolsados** - menta gestiona automáticamente los reembolsos si se notifica la cancelación a tiempo.
2. **Los pagos a vendedores se detienen** - si no se informa la cancelación, los vendedores recibirán el pago como si el evento se hubiera realizado.
3. **No hay recuperación posterior** - una vez que los fondos son distribuidos, no es posible revertir las transacciones.

### Cómo notificar una cancelación

{% conditionaltabs id="tabs-cancellation" %}

{% tab label="API de menta tech" %}
### Cancelación vía API (Recomendado)

Para cancelar un evento mediante la API, utiliza el endpoint de cancelación:

```bash
POST /v1/events/:externalReferenceId/cancellation
```

Donde `:externalReferenceId` es el identificador único del evento en tu sistema de ticketing.

**Parámetros opcionales:**
- `showId`: Si solo deseas cancelar un show específico dentro del evento, incluye este parámetro en el query string.

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

{% /tab %}

{% tab label="Endpoint expuesto por tu plataforma" %}
### Cancelación con integración Pull

Si tu integración es mediante un endpoint que menta consulta periódicamente, tienes dos opciones:

**Opción 1: Feed de eventos cancelados**
Exponer un endpoint adicional (ej: `https://api.tuplataforma.com/menta-feed/cancelled`) que devuelva la lista de eventos cancelados. menta consultará este feed periódicamente.

```json
[
  {
    "externalReferenceId": "evt_example_001",
    "cancelledAt": "2025-01-15T10:30:00.000Z",
    "showId": "show_001" // opcional, si es solo un show
  }
]
```

**Opción 2: API Call directo (Recomendado)**
Aunque tu integración principal sea mediante pull, **recomendamos utilizar el API call de cancelación** para notificaciones en tiempo real:

```bash
POST /v1/events/:externalReferenceId/cancellation
```

Esto garantiza que la cancelación se procese inmediatamente sin esperar al siguiente ciclo de polling.
{% /tab %}

{% tab label="Carga manual de eventos" %}
### Cancelación con carga manual

Para integraciones de carga manual, tienes dos opciones:

**Opción 1: API Call (Recomendado)**
Aunque la carga de eventos sea manual, recomendamos implementar el API call de cancelación:

```bash
POST /v1/events/:externalReferenceId/cancellation
```

**Opción 2: Notificación al Account Manager**
Dado que este tipo de integración está orientado a plataformas con menor volumen, también es posible informar la cancelación directamente a tu account manager. Sin embargo, esto puede generar demoras en el procesamiento.

{% callout type="info" %}
Contacta a tu account manager lo antes posible cuando un evento sea cancelado para evitar pagos incorrectos.
{% /callout %}
{% /tab %}

{% /conditionaltabs %}
