openapi: 3.0.3
info:
  title: API de Digitra para partners
  version: '2026-10-01'
  description: |
    Crea reservas con tu propio identificador, obtén el enlace de registro del
    huésped, recibe webhooks de cada avance y consulta, modifica o cancela
    reservas.

    La llave decide el ambiente: `dgt_test_` (pruebas, TRA y SIRE simulados) o
    `dgt_live_` (producción). Guía completa en `guia-integracion.md`; webhooks en
    `webhooks.md`.
  contact:
    name: Soporte Digitra
    email: soporte@digitra.rent
servers:
  - url: https://digitra.rentals
    description: Producción y pruebas (la llave decide el ambiente)
security:
  - bearerAuth: []
tags:
  - name: Propiedades
    description: Inventario de la llave y mapeo con el id de tu PMS
  - name: Reservas
    description: Crear, modificar y cancelar reservas
  - name: Registro
    description: Estado del registro de huéspedes y de los reportes TRA y SIRE
  - name: Webhooks
    description: Endpoints, secretos, pruebas e historial de entregas

paths:
  /api/partners/properties:
    get:
      tags: [Propiedades]
      summary: Listar las propiedades de la llave
      operationId: listProperties
      responses:
        '200':
          description: Propiedades del ambiente de la llave
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/RateLimitLimit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/RateLimitRemaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/RateLimitReset' }
          content:
            application/json:
              schema:
                type: object
                required: [properties]
                properties:
                  properties:
                    type: array
                    items: { $ref: '#/components/schemas/Property' }
        '401': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
    patch:
      tags: [Propiedades]
      summary: Asociar propiedades con el id de tu PMS (p. ej. listing_id de Guesty)
      description: Todo o nada. `external_id` en null quita la asociación.
      operationId: mapProperties
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [mappings]
              properties:
                mappings:
                  type: array
                  minItems: 1
                  maxItems: 500
                  items:
                    type: object
                    required: [property_id, external_id]
                    properties:
                      property_id: { type: string, format: uuid }
                      external_id: { type: string, maxLength: 120, nullable: true }
            example:
              mappings:
                - property_id: 3f1c2b9a-0d4e-4a51-9a7e-2b6f0c8d1e23
                  external_id: 64f1a2b3c4d5e6f7a8b9c0d1
      responses:
        '200':
          description: Mapeo aplicado
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  mappings:
                    type: array
                    items:
                      type: object
                      properties:
                        property_id: { type: string, format: uuid }
                        external_id: { type: string, nullable: true }
        '400': { $ref: '#/components/responses/Error' }
        '403': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }

  /api/partners/reservations:
    post:
      tags: [Reservas]
      summary: Crear una reserva y obtener el enlace de registro
      description: |
        Idempotente por `external_id`: el mismo cuerpo devuelve 200 con la misma
        reserva; otro cuerpo con el mismo `external_id` devuelve 409
        `reservation_conflict`.
      operationId: createReservation
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateReservation' }
            example:
              external_id: 65a1b2c3d4e5f6a7b8c9d0e1
              source: guesty
              property_external_id: 64f1a2b3c4d5e6f7a8b9c0d1
              check_in: '2026-10-10'
              check_out: '2026-10-12'
              guest_name: María Pérez
              guests_count: 2
              guest_email: huesped@ejemplo.com
              metadata: { canal: airbnb }
      responses:
        '201':
          description: Reserva creada
          headers:
            X-RateLimit-Limit: { $ref: '#/components/headers/RateLimitLimit' }
            X-RateLimit-Remaining: { $ref: '#/components/headers/RateLimitRemaining' }
            X-RateLimit-Reset: { $ref: '#/components/headers/RateLimitReset' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReservationResponse' }
        '200':
          description: Reintento idéntico; la misma reserva (`idempotent_replay` true)
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReservationResponse' }
        '400': { $ref: '#/components/responses/Error' }
        '401': { $ref: '#/components/responses/Error' }
        '403': { $ref: '#/components/responses/Error' }
        '404': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
        '503': { $ref: '#/components/responses/Error' }
    patch:
      tags: [Reservas]
      summary: Modificar una reserva
      description: |
        Con el TRA ya reportado (se reporta el día de llegada) solo se puede
        ampliar la salida; las noches nuevas se registran ante MinCIT
        automáticamente. Mover la llegada, acortar la estadía o cambiar fechas
        cuya entrada ya se reportó a SIRE responde 409 `already_reported`. Una
        reserva cancelada responde 409 `reservation_cancelled`.
      operationId: updateReservation
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UpdateReservation' }
            example: { external_id: 65a1b2c3d4e5f6a7b8c9d0e1, check_out: '2026-10-13', guests_count: 3 }
      responses:
        '200':
          description: Estado actualizado
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ReservationResponse'
                  - type: object
                    properties:
                      changed:
                        type: array
                        items: { type: string, example: dates }
        '400': { $ref: '#/components/responses/Error' }
        '404': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }

  /api/partners/reservations/cancel:
    post:
      tags: [Reservas]
      summary: Cancelar una reserva
      description: |
        Una reserva cancelada no se reporta a TRA ni a SIRE. Idempotente. Si algo
        ya se había reportado, `already_reported` lo indica.
      operationId: cancelReservation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [external_id]
              properties:
                external_id: { type: string, maxLength: 120 }
                reason: { type: string, maxLength: 500 }
            example: { external_id: 65a1b2c3d4e5f6a7b8c9d0e1, reason: Cancelada en Guesty }
      responses:
        '200':
          description: Cancelada
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/RegistrationStatus'
                  - type: object
                    properties:
                      already_cancelled: { type: boolean }
                      already_reported:
                        type: object
                        properties:
                          tra: { type: boolean }
                          sire: { type: boolean }
                      warning: { type: string }
        '404': { $ref: '#/components/responses/Error' }

  /api/partners/registrations:
    get:
      tags: [Registro]
      summary: Consultar el estado por external_id
      operationId: getRegistration
      parameters:
        - name: external_id
          in: query
          required: true
          schema: { type: string }
        - name: include
          in: query
          description: '`guests` agrega los datos de cada huésped registrado.'
          schema: { type: string, enum: [guests] }
      responses:
        '200':
          description: Estado de la reserva
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RegistrationDetail' }
        '404': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }
    post:
      tags: [Registro]
      summary: Registrar huéspedes capturados en tu propio formulario
      description: |
        Flujo alternativo al enlace: tu sistema captura los datos de todos los
        huéspedes y los envía de una vez. Crea la reserva, cierra el registro y
        programa TRA y SIRE.
      operationId: submitRegistration
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SubmitRegistration' }
      responses:
        '201':
          description: Registro creado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RegistrationStatus' }
        '200':
          description: Reintento; el mismo registro
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RegistrationStatus' }
        '400': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }

  /api/partners/registrations/{external_id}:
    get:
      tags: [Registro]
      summary: Consultar el estado (id en la ruta)
      description: Si tu id lleva `/`, usa la forma con `?external_id=`.
      operationId: getRegistrationByPath
      parameters:
        - name: external_id
          in: path
          required: true
          schema: { type: string }
        - name: include
          in: query
          schema: { type: string, enum: [guests] }
      responses:
        '200':
          description: Estado de la reserva
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RegistrationDetail' }
        '404': { $ref: '#/components/responses/Error' }

  /api/partners/webhooks:
    get:
      tags: [Webhooks]
      summary: Listar endpoints
      operationId: listWebhooks
      responses:
        '200':
          description: Endpoints activos
          content:
            application/json:
              schema:
                type: object
                properties:
                  endpoints:
                    type: array
                    items: { $ref: '#/components/schemas/WebhookEndpoint' }
    post:
      tags: [Webhooks]
      summary: Registrar un endpoint
      description: El `secret` se devuelve solo en esta respuesta.
      operationId: createWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string, format: uri, description: HTTPS pública }
                events:
                  type: array
                  items: { $ref: '#/components/schemas/EventType' }
                include_guests: { type: boolean, default: false }
            example: { url: 'https://tu-servidor.com/webhooks/digitra', events: ['*'], include_guests: true }
      responses:
        '201':
          description: Endpoint creado
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/WebhookEndpoint'
                  - type: object
                    required: [secret]
                    properties:
                      secret: { type: string, example: whsec_xxxxxxxxxxxxxxxxxxxxxxxx }
        '400': { $ref: '#/components/responses/Error' }
        '409': { $ref: '#/components/responses/Error' }

  /api/partners/webhooks/{id}:
    parameters:
      - $ref: '#/components/parameters/WebhookId'
    patch:
      tags: [Webhooks]
      summary: Modificar un endpoint
      operationId: updateWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url: { type: string, format: uri }
                events:
                  type: array
                  items: { $ref: '#/components/schemas/EventType' }
                include_guests: { type: boolean }
      responses:
        '200':
          description: Endpoint actualizado
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WebhookEndpoint' }
        '404': { $ref: '#/components/responses/Error' }
    delete:
      tags: [Webhooks]
      summary: Desactivar un endpoint
      operationId: deleteWebhook
      responses:
        '200':
          description: Desactivado
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  id: { type: string }
                  is_active: { type: boolean }
        '404': { $ref: '#/components/responses/Error' }

  /api/partners/webhooks/{id}/rotate-secret:
    parameters:
      - $ref: '#/components/parameters/WebhookId'
    post:
      tags: [Webhooks]
      summary: Rotar el secreto de firma
      operationId: rotateWebhookSecret
      responses:
        '200':
          description: Secreto nuevo (se muestra una vez)
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  secret: { type: string }
        '404': { $ref: '#/components/responses/Error' }

  /api/partners/webhooks/{id}/test:
    parameters:
      - $ref: '#/components/parameters/WebhookId'
    post:
      tags: [Webhooks]
      summary: Enviar un evento ping firmado ahora mismo
      operationId: testWebhook
      responses:
        '200':
          description: Resultado del envío
          content:
            application/json:
              schema:
                type: object
                properties:
                  event_id: { type: string }
                  delivered: { type: boolean }
                  http_status: { type: integer, nullable: true }
                  duration_ms: { type: integer }
                  error: { type: string }
        '404': { $ref: '#/components/responses/Error' }

  /api/partners/webhooks/deliveries:
    get:
      tags: [Webhooks]
      summary: Historial de entregas
      operationId: listDeliveries
      parameters:
        - { name: status, in: query, schema: { type: string, enum: [pending, delivered, failed] } }
        - { name: event, in: query, schema: { type: string } }
        - { name: external_id, in: query, schema: { type: string } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 200, default: 50 } }
      responses:
        '200':
          description: Entregas, más reciente primero
          content:
            application/json:
              schema:
                type: object
                properties:
                  deliveries:
                    type: array
                    items: { $ref: '#/components/schemas/Delivery' }

  /api/partners/webhooks/deliveries/{id}/redeliver:
    parameters:
      - $ref: '#/components/parameters/WebhookId'
    post:
      tags: [Webhooks]
      summary: Reenviar un evento (mismo event_id, ciclo de reintentos nuevo)
      operationId: redeliver
      responses:
        '200':
          description: Encolado
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  id: { type: string }
                  event_id: { type: string }
                  status: { type: string, example: pending }
        '404': { $ref: '#/components/responses/Error' }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: '`dgt_test_…` (pruebas) o `dgt_live_…` (producción)'

  parameters:
    WebhookId:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }

  headers:
    RateLimitLimit:
      description: Peticiones permitidas por minuto
      schema: { type: integer }
    RateLimitRemaining:
      description: Peticiones que quedan en la ventana
      schema: { type: integer }
    RateLimitReset:
      description: Cuándo se reinicia la ventana (segundos Unix)
      schema: { type: integer }

  responses:
    Error:
      description: Error
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }

  schemas:
    Error:
      type: object
      required: [error, code]
      properties:
        error: { type: string, description: Mensaje legible (puede cambiar) }
        code: { type: string, description: Código estable, example: reservation_conflict }
        details: { type: object, additionalProperties: true }

    Property:
      type: object
      properties:
        property_id: { type: string, format: uuid }
        external_id: { type: string, nullable: true, description: Id en tu PMS }
        rnt_id: { type: string, nullable: true }
        name: { type: string }
        internal_name: { type: string, nullable: true }
        checkin_enabled: { type: boolean }
        compliance:
          type: object
          properties:
            tra: { type: boolean }
            sire: { type: boolean }
        requires_contract_signature: { type: boolean }
        requires_document_photos: { type: boolean }

    CreateReservation:
      type: object
      required: [external_id, check_in, check_out, guest_name, guests_count]
      description: Envía exactamente uno de property_id, property_external_id o rnt_id.
      properties:
        external_id: { type: string, maxLength: 120 }
        source: { type: string, maxLength: 50, example: guesty }
        property_id: { type: string, format: uuid }
        property_external_id: { type: string, maxLength: 120 }
        rnt_id: { type: string }
        room_number: { type: string, maxLength: 50 }
        check_in: { type: string, format: date }
        check_out: { type: string, format: date }
        guest_name: { type: string, maxLength: 255 }
        guests_count: { type: integer, minimum: 1 }
        expected_guests: { type: integer, minimum: 1 }
        total_amount: { type: number, minimum: 0 }
        currency: { type: string, enum: [COP, USD] }
        guest_email: { type: string, format: email }
        guest_phone: { type: string, maxLength: 30 }
        notes: { type: string, maxLength: 2000 }
        metadata: { type: object, additionalProperties: true, description: Máx. 4 KB }
        sandbox_simulate:
          type: string
          enum: [tra_failed, sire_failed]
          description: Solo con llave de pruebas

    UpdateReservation:
      type: object
      required: [external_id]
      properties:
        external_id: { type: string }
        check_in: { type: string, format: date }
        check_out: { type: string, format: date }
        guests_count: { type: integer, minimum: 1, maximum: 50 }
        guest_name: { type: string }
        guest_email: { type: string, format: email, nullable: true }
        guest_phone: { type: string, nullable: true }
        notes: { type: string, nullable: true }
        metadata: { type: object, additionalProperties: true }

    SubmitRegistration:
      type: object
      required: [external_id, check_in, check_out, guest_name, guests_count, motivo, tipo_acomodacion, guests]
      properties:
        external_id: { type: string }
        source: { type: string }
        property_id: { type: string, format: uuid }
        property_external_id: { type: string }
        rnt_id: { type: string }
        room_number: { type: string }
        check_in: { type: string, format: date }
        check_out: { type: string, format: date }
        guest_name: { type: string }
        guests_count: { type: integer }
        expected_guests: { type: integer }
        total_amount: { type: number }
        currency: { type: string, enum: [COP, USD] }
        motivo:
          type: string
          enum: ['Vacaciones, recreo y ocio', Trabajo, 'Visita a familiares y a amigos', 'Educación y formación', 'Salud y atención médica', 'Religión / peregrinaciones', Compras, Tránsito, 'Otros motivos']
        tipo_acomodacion:
          type: string
          example: Apartamento
        metadata: { type: object, additionalProperties: true }
        sandbox_simulate: { type: string, enum: [tra_failed, sire_failed] }
        guests:
          type: array
          minItems: 1
          maxItems: 30
          items: { $ref: '#/components/schemas/GuestInput' }

    DocumentType:
      type: string
      enum: [Pasaporte, C.C, C.E, T.I, R.C, D.E, C.D, PPT]
      description: 'D.E = documento extranjero (Mercosur/CAN), C.D = carné diplomático, PPT = Permiso por Protección Temporal'

    GuestInput:
      type: object
      required: [tipo_documento, numero_documento, nombre, apellido, nacionalidad, pais_procedencia, ciudad_procedencia, pais_residencia, ciudad_residencia]
      properties:
        tipo_documento: { $ref: '#/components/schemas/DocumentType' }
        numero_documento: { type: string, pattern: '^[a-zA-Z0-9]+$' }
        nombre: { type: string }
        apellido: { type: string }
        nacionalidad: { type: string, description: ISO 3166-1 alpha-2, example: CO }
        fecha_nacimiento: { type: string, format: date, description: Obligatoria para documentos de extranjero }
        pais_procedencia: { type: string, example: US }
        ciudad_procedencia: { type: string }
        pais_residencia: { type: string, example: US }
        ciudad_residencia: { type: string }
        email: { type: string, format: email }
        phone: { type: string }
        address: { type: string }
        doc_photo_front_url: { type: string, format: uri }
        doc_photo_back_url: { type: string, format: uri }

    Guest:
      type: object
      properties:
        is_primary: { type: boolean }
        tipo_documento: { $ref: '#/components/schemas/DocumentType' }
        numero_documento: { type: string }
        nombre: { type: string }
        apellido: { type: string }
        nacionalidad: { type: string, nullable: true }
        fecha_nacimiento: { type: string, format: date, nullable: true }
        email: { type: string, nullable: true }
        phone: { type: string, nullable: true }
        address: { type: string, nullable: true }
        ciudad_residencia: { type: string, nullable: true }
        pais_residencia: { type: string, nullable: true }
        registered_at: { type: string, format: date-time, nullable: true }
        document_uploaded:
          type: boolean
          nullable: true
          description: "Si el huésped subió la foto del documento (la imagen nunca viaja por la API). `null` cuando la salida fue hace más de 30 días: las fotos se borran por política de retención y ya no se puede saber."

    Report:
      type: object
      properties:
        status: { type: string, example: pending }
        reported_at: { type: string, format: date-time, nullable: true }
        warning: { type: string }
        simulated: { type: boolean, description: Solo en pruebas }

    RegistrationStatus:
      type: object
      properties:
        ok: { type: boolean }
        reservation_id: { type: string, description: Id de Digitra }
        external_id: { type: string }
        status:
          type: string
          enum: [pending_checkin, pending_signature, pending_compliance, compliant, partial, failed, cancelled]
        reservation_status: { type: string, enum: [confirmed, cancelled] }
        sandbox: { type: boolean }
        checkin_token: { type: string, nullable: true }
        checkin_status: { type: string, nullable: true }
        guests_registered: { type: integer }
        tra:
          allOf:
            - $ref: '#/components/schemas/Report'
            - type: object
              properties:
                sends_on: { type: string, format: date, nullable: true, description: 'Día en que sale el TRA: la fecha de llegada. Si el registro se completa después de la llegada, sale en la siguiente corrida.' }
                mincit_code: { type: integer, nullable: true }
        sire:
          allOf:
            - $ref: '#/components/schemas/Report'
            - type: object
              properties:
                foreign_guests: { type: integer }

    ReservationResponse:
      allOf:
        - $ref: '#/components/schemas/RegistrationStatus'
        - type: object
          properties:
            source: { type: string, nullable: true }
            metadata: { type: object, additionalProperties: true }
            checkin_url: { type: string, format: uri, nullable: true, description: Enlace para el huésped }
            created_at: { type: string, format: date-time }
            idempotent_replay: { type: boolean }

    RegistrationDetail:
      allOf:
        - $ref: '#/components/schemas/ReservationResponse'
        - type: object
          properties:
            property_id: { type: string, format: uuid }
            check_in: { type: string, format: date }
            check_out: { type: string, format: date }
            guests_count: { type: integer }
            completed_at: { type: string, format: date-time, nullable: true, description: Cuándo se completó el registro }
            guests:
              type: array
              description: Solo con include=guests
              items: { $ref: '#/components/schemas/Guest' }

    EventType:
      type: string
      enum:
        - '*'
        - reservation.created
        - reservation.updated
        - reservation.cancelled
        - registration.updated
        - registration.completed
        - contract.signed
        - tra.reported
        - tra.failed
        - sire.reported
        - sire.failed
        - ping

    WebhookEndpoint:
      type: object
      properties:
        id: { type: string, format: uuid }
        url: { type: string, format: uri }
        events:
          type: array
          items: { $ref: '#/components/schemas/EventType' }
        include_guests: { type: boolean }
        is_active: { type: boolean }
        created_at: { type: string, format: date-time }
        secret_rotated_at: { type: string, format: date-time, nullable: true }

    Delivery:
      type: object
      properties:
        id: { type: string, format: uuid }
        endpoint_id: { type: string, format: uuid }
        event_id: { type: string }
        event: { type: string }
        status: { type: string, enum: [pending, delivered, failed] }
        attempts: { type: integer }
        last_http_status: { type: integer, nullable: true }
        last_error: { type: string, nullable: true }
        next_attempt_at: { type: string, format: date-time, nullable: true }
        delivered_at: { type: string, format: date-time, nullable: true }
        created_at: { type: string, format: date-time }
        attempt_log:
          type: array
          items:
            type: object
            properties:
              attempted_at: { type: string, format: date-time }
              http_status: { type: integer, nullable: true }
              duration_ms: { type: integer }
              error: { type: string, nullable: true }

    WebhookEvent:
      description: |
        Cuerpo del POST que Digitra envía a tu URL. Headers: X-Digitra-Event-Id,
        X-Digitra-Event, X-Digitra-Timestamp (segundos Unix) y
        X-Digitra-Signature (sha256= + HMAC-SHA256 de "{timestamp}.{cuerpo}").
        Ver webhooks.md.
      type: object
      required: [id, type, created_at, api_version, sandbox, data]
      properties:
        id: { type: string, example: evt_3f9a1c2b4d5e6f708192a3b4c5d6e7f8 }
        type: { $ref: '#/components/schemas/EventType' }
        created_at: { type: string, format: date-time }
        api_version: { type: string, example: '2026-10-01' }
        sandbox: { type: boolean }
        data:
          type: object
          properties:
            reservation_id: { type: string }
            external_id: { type: string }
            source: { type: string, nullable: true }
            property_id: { type: string, format: uuid }
            reservation_status: { type: string }
            status: { type: string }
            check_in: { type: string, format: date }
            check_out: { type: string, format: date }
            guests_count: { type: integer }
            checkin_url: { type: string, format: uri, nullable: true }
            checkin_status: { type: string, nullable: true }
            guests_registered: { type: integer }
            contract_signed: { type: boolean }
            tra: { $ref: '#/components/schemas/Report' }
            sire: { $ref: '#/components/schemas/Report' }
            metadata: { type: object, additionalProperties: true }
            guests:
              type: array
              description: Solo en registration.* con include_guests
              items: { $ref: '#/components/schemas/Guest' }
