> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ventry.es/llms.txt
> Use this file to discover all available pages before exploring further.

# Crear entradas

> Da de alta una o varias entradas (máximo 500 por llamada).

**Es una operación de todo o nada**: si algún código ya existe o alguna referencia no es válida, no se crea ninguna. Reintenta con el lote corregido.

Exige la cabecera `Idempotency-Key`. Repetir la llamada con la misma clave devuelve la respuesta original sin crear nada nuevo.



## OpenAPI

````yaml /openapi.json post /tickets
openapi: 3.1.0
info:
  title: VENTRY API
  version: 1.0.0
  description: >-
    API pública de **VENTRY** para integradores externos.


    Permite volcar entradas desde una plataforma de ticketing, acreditar
    asistentes

    desde una aplicación propia y consultar el estado del evento en tiempo real.


    ## Autenticación


    Todas las llamadas viajan con una API key en la cabecera `Authorization`:


    ```

    Authorization: Bearer vk_live_8Kq2...

    ```


    La clave se genera desde el panel de VENTRY y **se muestra una sola vez**.
    Si se

    pierde, hay que emitir otra. Las claves que piden permisos de escritura
    nacen

    desactivadas y necesitan que un administrador del evento las habilite a
    mano.


    ## Un despliegue, un evento


    Cada instalación de VENTRY corresponde a **un único evento**. La API key ya

    determina de qué evento se está hablando, por eso ningún endpoint pide un

    identificador de evento. Una clave puede además estar limitada a ciertos
    tipos

    de entrada o a ciertos días: lo que quede fuera de ese ámbito se comporta
    como

    si no existiera.


    ## Convenciones


    - **Importes**: enteros en **céntimos**. `1250` son 12,50 €. La moneda está
    en `GET /event`.

    - **Fechas**: ISO 8601 en UTC. El huso horario del evento está en `GET
    /event`.

    - **Identificadores**: cadenas de 24 caracteres hexadecimales.

    - **Campos**: `snake_case`.


    ### `occurred_at` frente a `created_at`


    Los terminales de VENTRY siguen cobrando sin cobertura y sincronizan
    después.

    Por eso las ventas y recargas llevan dos marcas: `occurred_at` es cuándo
    pasó

    de verdad y `created_at` cuándo llegó al servidor. **Para agrupar por tiempo
    o

    cuadrar caja hay que usar siempre `occurred_at`**; una venta de las 22:00

    sincronizada a las 02:00 pertenece a la noche del viernes, no a la del
    sábado.


    ## Paginación


    Los listados devuelven `{ object: "list", data, has_more, next_cursor }`.
    Para

    la siguiente página se repite la llamada con `starting_after=<next_cursor>`.

    No hay números de página: el cursor es estable aunque se sigan creando
    registros

    mientras se recorre el listado.


    Para sincronizar de forma incremental, combina `updated_since` con el
    cursor.


    ## Idempotencia


    Las escrituras exigen la cabecera `Idempotency-Key` con un UUID generado por

    el cliente. Repetir la misma llamada con la misma clave devuelve la
    respuesta

    original sin volver a ejecutar nada, así que un reintento tras un fallo de
    red

    nunca duplica una entrada ni una recarga. Reutilizar la clave con un cuerpo

    distinto devuelve `422 idempotency_key_reused`.


    ## Límites


    Por defecto **120 peticiones por minuto** y clave, y **20 por minuto** para
    las

    escrituras. Cada respuesta lleva `x-ratelimit-remaining` y, al agotarse,

    `retry-after` con los segundos que faltan.


    ## Errores


    Todos los errores comparten la misma forma y llevan un `request_id` que

    conviene registrar: con él, el soporte de VENTRY encuentra la petición
    exacta.
  contact:
    name: Soporte VENTRY
    url: https://ventry.es
servers:
  - url: http://localhost:3000/v1
    description: Servidor del evento
security:
  - apiKey: []
tags:
  - name: Evento
    description: >-
      Configuración del evento: días, zonas y tipos de entrada. Es lo primero
      que hay que leer para mapear el catálogo propio contra el de VENTRY.
  - name: Entradas
    description: >-
      Alta, consulta y anulación de entradas. El caso de uso principal de la
      API.
  - name: Accesos
    description: >-
      Acreditación en puerta y control de zonas. Requiere permisos de escritura
      activados manualmente.
  - name: Cashless
    description: >-
      Saldo de pulseras, consumiciones y recargas. Todos los importes en
      céntimos.
  - name: Informes
    description: Agregados de venta, coherentes con el cierre de caja del panel.
  - name: Meta
    description: Comprobación de credenciales y documentación.
paths:
  /tickets:
    post:
      tags:
        - Entradas
      summary: Crear entradas
      description: >-
        Da de alta una o varias entradas (máximo 500 por llamada).


        **Es una operación de todo o nada**: si algún código ya existe o alguna
        referencia no es válida, no se crea ninguna. Reintenta con el lote
        corregido.


        Exige la cabecera `Idempotency-Key`. Repetir la llamada con la misma
        clave devuelve la respuesta original sin crear nada nuevo.
      parameters:
        - schema:
            type: string
          in: header
          name: idempotency-key
          required: true
          description: Identificador único de la operación, generado por tu sistema.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              oneOf:
                - type: object
                  required:
                    - ticket_type
                    - valid_days
                    - attendee
                  properties:
                    code:
                      type: string
                      minLength: 4
                      maxLength: 64
                      description: >-
                        Código del QR. Si se omite, VENTRY genera uno. Conviene
                        enviar el propio para poder reconciliar después.
                    ticket_type:
                      type: string
                      description: Id de `GET /ticket-types`.
                    valid_days:
                      type: array
                      minItems: 1
                      items:
                        type: string
                      description: Ids de `GET /days` en los que la entrada es válida.
                    attendee:
                      type: object
                      required:
                        - name
                        - email
                      properties:
                        name:
                          type: string
                          minLength: 1
                          maxLength: 200
                        email:
                          type: string
                          format: email
                          maxLength: 200
                        document_type:
                          type: string
                          maxLength: 40
                          description: Tipo de documento (DNI, NIE, PASSPORT…). Opcional.
                        document_number:
                          type: string
                          maxLength: 60
                          description: >-
                            Número de documento. Se almacena para la
                            acreditación pero nunca se devuelve completo.
                        date_of_birth:
                          type: string
                          format: date
                          description: >-
                            Fecha de nacimiento (YYYY-MM-DD). Si se omite, se
                            asume mayor de edad.
                    extra_zones:
                      type: array
                      items:
                        type: string
                      description: >-
                        Zonas adicionales concedidas a esta entrada por
                        complementos, además de las de su tipo.
                - type: object
                  required:
                    - tickets
                  properties:
                    tickets:
                      type: array
                      minItems: 1
                      maxItems: 500
                      items:
                        type: object
                        required:
                          - ticket_type
                          - valid_days
                          - attendee
                        properties:
                          code:
                            type: string
                            minLength: 4
                            maxLength: 64
                            description: >-
                              Código del QR. Si se omite, VENTRY genera uno.
                              Conviene enviar el propio para poder reconciliar
                              después.
                          ticket_type:
                            type: string
                            description: Id de `GET /ticket-types`.
                          valid_days:
                            type: array
                            minItems: 1
                            items:
                              type: string
                            description: >-
                              Ids de `GET /days` en los que la entrada es
                              válida.
                          attendee:
                            type: object
                            required:
                              - name
                              - email
                            properties:
                              name:
                                type: string
                                minLength: 1
                                maxLength: 200
                              email:
                                type: string
                                format: email
                                maxLength: 200
                              document_type:
                                type: string
                                maxLength: 40
                                description: >-
                                  Tipo de documento (DNI, NIE, PASSPORT…).
                                  Opcional.
                              document_number:
                                type: string
                                maxLength: 60
                                description: >-
                                  Número de documento. Se almacena para la
                                  acreditación pero nunca se devuelve completo.
                              date_of_birth:
                                type: string
                                format: date
                                description: >-
                                  Fecha de nacimiento (YYYY-MM-DD). Si se omite,
                                  se asume mayor de edad.
                          extra_zones:
                            type: array
                            items:
                              type: string
                            description: >-
                              Zonas adicionales concedidas a esta entrada por
                              complementos, además de las de su tipo.
      responses:
        '201':
          description: Entradas creadas.
          content:
            application/json:
              schema:
                type: object
                description: Entradas creadas.
                properties:
                  object:
                    type: string
                    enum:
                      - list
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Ticket'
                  has_more:
                    type: boolean
                    description: >-
                      Si es `true`, pide la siguiente página con
                      `starting_after`.
                  next_cursor:
                    type:
                      - 'null'
                      - string
                    description: Valor a pasar en `starting_after` para continuar.
                required:
                  - object
                  - data
                  - has_more
                  - next_cursor
        '400':
          description: Petición mal formada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: API key ausente, inválida o caducada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: La API key no tiene el permiso necesario.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Alguno de los códigos ya existe.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Referencias inválidas o fuera del ámbito de la clave.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Se ha superado el límite de peticiones.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Error interno.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Ticket:
      type: object
      properties:
        object:
          type: string
          enum:
            - ticket
        id:
          type: string
        code:
          type: string
          description: Código impreso en el QR. Es único en todo el evento.
          example: VTR-8F2K-1029
        status:
          type: string
          enum:
            - active
            - inactive
            - used
          description: >-
            `active` sin canjear · `used` canjeada por una pulsera · `inactive`
            anulada.
        ticket_type:
          type: object
          properties:
            id:
              type:
                - 'null'
                - string
            name:
              type:
                - 'null'
                - string
          required:
            - id
            - name
          description: Tipo de entrada.
        attendee:
          type: object
          description: >-
            Datos del asistente. El número de documento se devuelve enmascarado
            y la fecha de nacimiento nunca se expone.
          properties:
            name:
              type:
                - 'null'
                - string
            email:
              type:
                - 'null'
                - string
            document:
              type: object
              properties:
                type:
                  type:
                    - 'null'
                    - string
                last4:
                  type:
                    - 'null'
                    - string
        valid_days:
          type: array
          items:
            type: object
            properties:
              id:
                type:
                  - 'null'
                  - string
              name:
                type:
                  - 'null'
                  - string
            required:
              - id
              - name
            description: Día de validez.
        extra_zones:
          type: array
          items:
            type: object
            properties:
              id:
                type:
                  - 'null'
                  - string
              name:
                type:
                  - 'null'
                  - string
            required:
              - id
              - name
            description: Zona extra concedida a esta entrada por complementos.
        wristband:
          type:
            - 'null'
            - object
          properties:
            id:
              type:
                - 'null'
                - string
            nfc:
              type:
                - 'null'
                - string
        checked_in_at:
          type:
            - 'null'
            - string
        created_at:
          type:
            - 'null'
            - string
        updated_at:
          type:
            - 'null'
            - string
    Error:
      type: object
      description: >-
        Todos los errores de la API comparten esta forma. Ramifica sobre `code`,
        que es estable; `message` puede cambiar de redacción.
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
                - authentication_error
                - permission_error
                - invalid_request_error
                - not_found_error
                - conflict_error
                - rate_limit_error
                - api_error
            code:
              type: string
              example: ticket_not_found
            message:
              type: string
            param:
              type: string
              description: Campo de la petición que provocó el error, si aplica.
            request_id:
              type: string
              description: Identificador de la petición. Cítalo al contactar con soporte.
              example: req_9f2c1a4e7b304d51
          required:
            - type
            - code
            - message
            - request_id
      required:
        - error
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >-
        API key del integrador, emitida desde el panel de VENTRY. Formato
        `vk_live_...` en producción y `vk_test_...` en entornos de prueba.

````