openapi: 3.0.3

info:
  title: Partner Integration API — I4Move Rischio Volo
  version: "1.1.1"
  description: |
    OpenAPI specification for submitting collective enrolments for the product **I4Move (I4T)**
    — guarantee **Rischio Volo**. Also covers enrolment retrieval, cancellation (storno) and
    attachment management. Policy modification (endorsement) is **not available** for this
    product.

    ## Authentication
    All requests require a **Bearer Token** in the `Authorization` header.
    The token is obtained via `POST /api/v1/sicurezza/login` using credentials provided by the
    Overx team.

    ## General technical notes
    - Decimal separator: **dot** (`.`), e.g. `12.50`
    - Date format: **ISO 8601** (`YYYY-MM-DDTHH:MM:SSZ`)
    - Enums: strictly respect the case (Upper/Lower) indicated for each field
    - The `keyVartec` and `keyGaranzia` keys are **case-sensitive**
    - \*\* `tassePerc` (21.25%) is a **provisional** value — a breakdown across multiple tax
      rates is expected and this figure is not final.
  contact:
    name: Overx Integration Team
  license:
    name: Internal use — Confidential

servers:
  - url: "https://testoverx-api.elbassicurazioni.it/api/v1"
    description: Test environment
  - url: "{baseUrl}"
    description: UAT / Production environment — URL provided separately by the Overx team.
    variables:
      baseUrl:
        default: https://testoverx-api.elbassicurazioni.it/api/v1
        description: API base URL

security:
  - bearerAuth: []

# ─────────────────────────────────────────────────────────────────────────────
# PATHS
# ─────────────────────────────────────────────────────────────────────────────
paths:
  /sicurezza/login:
    post:
      operationId: login
      summary: Obtain a Bearer Token
      description: |
        Authenticates using the OverX application user (credentials provided separately by the
        Overx team) and returns the Bearer Token to use in the `Authorization` header of every
        other call. This is the standard OverX authentication mechanism — no I4Move-specific
        login step is required.
      tags:
        - Authentication
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - username
                - password
              properties:
                username:
                  type: string
                  example: "<username>"
                password:
                  type: string
                  format: password
                  example: "<password>"
      responses:
        "200":
          description: |
            Login successful. The response body structure is not documented in the available
            materials — verify the exact field containing the token with the Overx team before
            integrating.
        "401":
          description: Invalid credentials

  /adesioni:
    post:
      operationId: creaAdesione
      summary: Submit a new collective enrolment
      description: |
        Receives the payload of a new enrolment for the product I4Move (I4T) — guarantee
        `rischioVolo` — and persists it in the insurance system. The payload must include all
        mandatory fields described in the `AdesioneRequest` schema.
      tags:
        - Enrolments
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AdesioneRequest"
            examples:
              AdesioneSample:
                $ref: "#/components/examples/AdesioneSample"
      responses:
        "201":
          description: |
            Enrolment created successfully.
            The response body structure is not documented in the available materials.
            Verify the response contract with the Overx team before integrating.
        "400":
          description: Invalid payload (missing mandatory fields, wrong format, violated constraints)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Bearer token missing, expired or invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Valid token but lacking the required permissions
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          description: Unprocessable entity — semantically inconsistent data (e.g. dataScadenza <= dataEffetto, underwriting window rule violated)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /collettive/adesioni/by-num-polizza/by-num-adesione:
    get:
      operationId: recuperaAdesione
      summary: Retrieve an enrolment by policy and enrolment number
      description: |
        Returns the full detail of an enrolment (`AdesioneDTO`), echoing the same fields
        submitted in `AdesioneRequest` (contractor, insured asset, guarantees), enriched with
        REVO-assigned identifiers (`id`, `numeroAdesione`). The real response also includes
        internal blocks (policy, intermediary, product configuration) not documented here as
        they are not needed for partner integration.
      tags:
        - Enrolments
      parameters:
        - name: numPolizza
          in: query
          required: true
          schema:
            type: string
          description: Master policy number.
        - name: numAdesione
          in: query
          required: true
          schema:
            type: string
          description: Enrolment number.
      responses:
        "200":
          description: Enrolment retrieved successfully. See the integration guide for a trimmed response example.
        "401":
          description: Bearer token missing, expired or invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Enrolment not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /movimenti/create-by-adesione:
    post:
      operationId: stornoAdesione
      summary: Cancel (storno) an existing enrolment
      description: |
        Registers a cancellation movement for a previously created enrolment. This is an
        applicative movement, not a physical deletion — the enrolment stays tracked in the
        system with its status and economic effects updated. Any blockchain records associated
        with the enrolment are automatically deactivated by REVO systems as part of this
        operation; the partner does not need to make any additional blockchain-related call.
      tags:
        - Enrolments
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StornoRequest"
      responses:
        "200":
          description: Cancellation movement registered successfully
        "400":
          description: Invalid request or missing data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Bearer token missing, expired or invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Enrolment not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Internal server error during cancellation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /collettive/allegato/upload:
    post:
      operationId: uploadAllegato
      summary: Upload an attachment for an enrolment
      description: |
        Receives the file to upload and its metadata for an existing enrolment, stores it on
        S3, and persists the attachment record (identifier, original name, extension, S3 path,
        enrolment reference). Optional — only needed if REVO systems handle document management
        for this partner, rather than the partner itself.
      tags:
        - Attachments
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "200":
          description: Attachment uploaded successfully — response includes the storage key needed for subsequent operations.
        "401":
          description: Bearer token missing, expired or invalid

  /collettive/allegato/download:
    post:
      operationId: downloadAllegato
      summary: Download a previously uploaded attachment
      description: |
        Receives the attachment key (S3 path), retrieves its metadata, downloads the file from
        S3 and returns the binary content with the original file name and extension
        (`Content-Disposition: attachment`).
      tags:
        - Attachments
      responses:
        "200":
          description: File downloaded successfully
        "400":
          description: Invalid attachment ID
        "404":
          description: Attachment not found
        "500":
          description: Internal server error during download

  /collettive/allegato/delete/{idAllegato}:
    delete:
      operationId: deleteAllegato
      summary: Delete an attachment
      description: |
        Deletes the physical file from S3 and removes the corresponding database record.
        **Irreversible.**
      tags:
        - Attachments
      parameters:
        - name: idAllegato
          in: path
          required: true
          schema:
            type: string
          description: Unique identifier of the attachment.
      responses:
        "200":
          description: Attachment deleted successfully
        "404":
          description: Attachment not found
        "500":
          description: Internal server error during deletion

# ─────────────────────────────────────────────────────────────────────────────
# COMPONENTS
# ─────────────────────────────────────────────────────────────────────────────
components:

  # ── Security ──────────────────────────────────────────────────────────────
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Token obtained via `POST /api/v1/sicurezza/login` using credentials provided by the
        Overx team.
        Include in the HTTP header:
        ```
        Authorization: Bearer <token>
        ```

  # ── Schemas ───────────────────────────────────────────────────────────────
  schemas:

    # ── Root request ─────────────────────────────────────────────────────────
    AdesioneRequest:
      type: object
      description: |
        Full payload for submitting a collective enrolment for the product I4Move (I4T) — guarantee `rischioVolo`.

        **Mutual exclusivity**: exactly one of `personaFisica`, `personaGiuridica` or
        `dittaIndividuale` must be present, matching `tipoSoggetto`.
      required:
        - keyPolizza
        - numeroPolizza
        - dataCreazione
        - dataEffetto
        - dataScadenza
        - tacitoRinnovo
        - source
        - stato
        - codiceProdotto
        - tipoSoggetto
        - beneAssicurato
        - garanzie
      properties:
        keyPolizza:
          type: string
          format: uuid
          description: Internal unique identifier of the policy (UUID v4).
          example: "749115fa-a5c9-4c98-9461-f10b7a6ae559"
        numeroAdesioneEsterno:
          type: string
          description: |
            Enrolment identifier on the partner side (optional).
            Free string — recommended to use a traceable format (e.g. ADH-YYYY-NNNNN).
          example: "ADH-2026-00123"
        numeroPolizza:
          type: string
          description: "Policy number, provided separately by the Overx team."
          example: "OXC0XXX"
        dataCreazione:
          type: string
          format: date-time
          description: Enrolment creation timestamp (ISO 8601).
          example: "2026-04-10T12:22:27.871Z"
        dataEffetto:
          type: string
          format: date-time
          description: |
            Policy effective date (ISO 8601).
            **Constraint**: must be >= today's date.
          example: "2026-04-24T00:00:00.000Z"
        dataScadenza:
          type: string
          format: date-time
          description: |
            Policy expiry date (ISO 8601).
            **Constraint**: must be > `dataEffetto`.
          example: "2026-04-30T00:00:00.000Z"
        tacitoRinnovo:
          type: boolean
          description: Indicates whether the policy renews automatically at expiry.
          default: false
          example: false
        source:
          type: string
          description: Request origin channel.
          example: "FE"
        stato:
          type: string
          description: Initial status of the enrolment at the time of submission.
          example: "EMESSA"
        codiceProdotto:
          type: string
          description: Insurance product code.
          enum: ["A001"]
          example: "A001"
        tipoSoggetto:
          type: string
          description: Type of policyholder.
          enum:
            - PERSONA_FISICA
            - PERSONA_GIURIDICA
            - DITTA_INDIVIDUALE
          example: "PERSONA_FISICA"
        personaFisica:
          $ref: "#/components/schemas/PersonaFisica"
        personaGiuridica:
          $ref: "#/components/schemas/PersonaGiuridica"
        dittaIndividuale:
          $ref: "#/components/schemas/DittaIndividuale"
        beneAssicurato:
          $ref: "#/components/schemas/BeneAssicurato"
        garanzie:
          type: array
          description: |
            List of activated coverages with their premiums. For I4Move this array always
            contains exactly one entry: `rischioVolo` — the sole guarantee of this product,
            flagged as "Garanzia base" and mandatory for the sale of the product.
          minItems: 1
          maxItems: 1
          items:
            $ref: "#/components/schemas/Garanzia"

    # ── Persona Fisica ────────────────────────────────────────────────────────
    PersonaFisica:
      type: object
      description: Personal details of the individual policyholder.
      required:
        - codiceFiscale
        - nome
        - cognome
        - nazione
      properties:
        codiceFiscale:
          type: string
          description: Italian Tax Code (16 alphanumeric characters).
          pattern: "^[A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST]{1}[0-9LMNPQRSTUV]{2}[A-Z]{1}[0-9LMNPQRSTUV]{3}[A-Z]{1}$"
          minLength: 16
          maxLength: 16
          example: "BNCMRC85M10L781A"
        nome:
          type: string
          description: First name of the policyholder.
          maxLength: 50
          example: "Marco"
        cognome:
          type: string
          description: Last name of the policyholder.
          maxLength: 50
          example: "Bianchi"
        nazione:
          type: string
          description: Country of residence (ISO 3166-1 alpha-3).
          minLength: 3
          maxLength: 3
          example: "ESP"
        indirizzo:
          type: string
          description: Full residential address.
          example: "Calle Mayor 15, 28013 Madrid"
        numeroTelefono:
          type: number
          description: Phone number (digits only, without international prefix).
          example: 612345678
        email:
          type: string
          format: email
          description: Policyholder email address (optional).
          example: "marco.bianchi@email.it"
        pec:
          type: string
          description: Policyholder PEC address (optional).
          example: ""

    # ── Persona Giuridica ─────────────────────────────────────────────────────
    PersonaGiuridica:
      type: object
      description: |
        Legal entity details of the policyholder.
        **Required fields are not confirmed by the source** — presumed mandatory by analogy
        with `PersonaFisica` (`ragioneSociale`, `partitaIva`, `nazione`), to be confirmed with
        the Overx product team before treating as binding.
      properties:
        partitaIva:
          type: string
          description: VAT number.
          example: "12345678901"
        formaSocietaria:
          type: string
          description: Legal company form (e.g. SPA, SRL).
          example: "SPA"
        ragioneSociale:
          type: string
          description: Registered company name.
          example: "Autotrasporti Verdi S.r.l."
        codiceAteco:
          type: string
          description: ATECO business activity code.
          example: "80.10"
        nazione:
          type: string
          description: Country (ISO 3166-1 alpha-3).
          minLength: 3
          maxLength: 3
          example: "ITA"
        indirizzo:
          type: string
          description: Full registered address.
          example: "{...}"
        numeroTelefono:
          type: number
          description: Phone number (digits only, without international prefix).
          example: 3278128564
        email:
          type: string
          format: email
          description: Company email address (optional).
          example: "email@email.com"
        pec:
          type: string
          description: Company PEC address.
          example: "pec@pec.com"

    # ── Ditta Individuale ─────────────────────────────────────────────────────
    DittaIndividuale:
      type: object
      description: |
        Sole proprietorship details of the policyholder.
        **Required fields are not confirmed by the source** — presumed mandatory by analogy
        with `PersonaFisica` (`codiceFiscale`, `nome`, `cognome`, `nazione`), to be confirmed
        with the Overx product team before treating as binding.
      properties:
        codiceFiscale:
          type: string
          description: Italian Tax Code (16 alphanumeric characters).
          minLength: 16
          maxLength: 16
          example: "RSSMRA85D15H501U"
        partitaIva:
          type: string
          description: VAT number.
          example: "12345678901"
        nome:
          type: string
          description: First name of the sole proprietor.
          example: "Mario"
        cognome:
          type: string
          description: Last name of the sole proprietor.
          example: "Rossi"
        nazione:
          type: string
          description: Country (ISO 3166-1 alpha-3).
          minLength: 3
          maxLength: 3
          example: "ITA"
        indirizzo:
          type: string
          description: Full registered address.
          example: "{...}"
        numeroTelefono:
          type: number
          description: Phone number (digits only, without international prefix).
          example: 32421134322
        email:
          type: string
          format: email
          description: Email address (optional).
          example: "email@email.com"
        pec:
          type: string
          description: PEC address.
          example: "pec@pec.com"

    # ── Bene Assicurato ───────────────────────────────────────────────────────
    BeneAssicurato:
      type: object
      description: Insured asset — for this policy the type is always `gruppo di assicurati`.
      required:
        - tipoBene
        - identificativo
        - variabiliTecniche
      properties:
        tipoBene:
          type: string
          description: Type of insured asset.
          enum: ["gruppo di assicurati"]
          example: "gruppo di assicurati"
        identificativo:
          type: string
          format: uuid
          description: Unique identifier of the insured asset (UUID v4).
          example: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
        variabiliTecniche:
          type: array
          description: |
            List of technical variables describing the travel risk. Each entry is identified by
            the `keyVartec` key (see table below).

            | `keyVartec`              | `valueVartec` type  | Required | Description                                                   |
            |:-------------------------|:--------------------|:---------|:--------------------------------------------------------------|
            | `prenotazione`           | date-time string    | Yes      | Date and time of the travel booking                           |
            | `destinazioneArea`       | enum string         | Yes      | Destination area: `italia`, `europa`, `mondo`                 |
            | `dataPartenza`           | date-time string    | Yes      | Departure date (start of coverage)                             |
            | `dataRientro`            | date-time string    | Yes      | Return date (end of coverage)                                  |
            | `destinazionePaese`      | string              | Yes      | Name of the main destination country                          |
            | `dataGridDatiAssicurato` | array of objects    | Yes      | List of insured persons (see `DatoAssicurato` schema)         |
            | `numeroAssicurati`       | integer             | Yes      | Total number of insured persons                               |
          items:
            $ref: "#/components/schemas/VariabileTecnicaBene"

    # ── Variabile Tecnica Bene ────────────────────────────────────────────────
    VariabileTecnicaBene:
      type: object
      description: Key-value pair representing a technical variable of the insured asset.
      required:
        - keyVartec
        - valueVartec
      properties:
        id:
          type: string
          format: uuid
          description: |
            Identifier of the technical variable (UUID v4). **Always overwritten/rebuilt by the
            backend** regardless of the value sent — may be omitted or sent as a placeholder.
        idBeneAssicurato:
          type: string
          format: uuid
          description: |
            Reference to the identifier of the parent insured asset. **Always
            overwritten/rebuilt by the backend** — may be omitted or sent as a placeholder.
        keyVartec:
          type: string
          description: Technical variable key (case-sensitive).
          enum:
            - prenotazione
            - destinazioneArea
            - dataPartenza
            - dataRientro
            - destinazionePaese
            - dataGridDatiAssicurato
            - numeroAssicurati
        valueVartec:
          description: |
            Value of the technical variable. The type depends on the key:
            - Date/time: ISO 8601 string
            - `destinazioneArea`: enum string (`italia` | `europa` | `mondo`)
            - `dataGridDatiAssicurato`: array of `DatoAssicurato` objects
            - `numeroAssicurati`: number
          oneOf:
            - type: string
            - type: number
            - type: integer
            - type: array
              items:
                $ref: "#/components/schemas/DatoAssicurato"

    # ── Dato Assicurato ───────────────────────────────────────────────────────
    DatoAssicurato:
      type: object
      description: |
        Data of a single insured person.
        Used as an element of the `valueVartec` array when `keyVartec` = `dataGridDatiAssicurato`.
      required:
        - cognomeAssicurato
        - nomeAssicurato
        - fasciaEta
      properties:
        cognomeAssicurato:
          type: string
          description: Last name of the insured person.
          example: "Bianchi"
        nomeAssicurato:
          type: string
          description: First name of the insured person.
          example: "Marco"
        fasciaEta:
          type: string
          description: |
            Age bracket of the insured person (LowerCase domain):
            - `bassa`
            - `media`
            - `alta`
          enum:
            - bassa
            - media
            - alta
          example: "media"
        email:
          type: string
          format: email
          description: Email of the insured person (optional).
          example: "marco.bianchi@email.it"

    # ── Garanzia ──────────────────────────────────────────────────────────────
    Garanzia:
      type: object
      description: Activated insurance coverage, with premiums and specific technical variables.
      required:
        - idGarOverx
        - nomeGaranzia
        - keyGaranzia
        - codAnagGar
        - ramoBilancio
        - premioGaranzia
      properties:
        id:
          type: string
          format: uuid
          description: |
            Identifier of the coverage in the enrolment (UUID v4). **Always overwritten by the
            backend** with a newly generated UUID, regardless of the value sent — may be
            omitted or sent as a placeholder.
        idGarOverx:
          type: string
          format: uuid
          description: |
            Coverage identifier in the Overx catalogue (UUID v4). **Required by REVO policy**:
            only the partner knows which coverage it intends to activate, so this must always be
            sent explicitly. Technical note: if omitted, the API would still populate it
            automatically from the catalogue (matched by key/name) — but this fallback must not
            be relied upon in production.
        nomeGaranzia:
          type: string
          description: Full name of the coverage (human-readable).
          example: "RISCHIO VOLO"
        keyGaranzia:
          type: string
          description: |
            Technical key of the coverage (case-sensitive). I4Move has a single guarantee,
            `rischioVolo` — the "Garanzia base", mandatory for the sale of the product and
            therefore always present in the `garanzie` array.
          enum:
            - rischioVolo
          example: "rischioVolo"
        codAnagGar:
          type: string
          description: Coverage registry code in the insurance system.
          example: "16032"
        ramoBilancio:
          type: string
          description: Insurance accounting branch.
          example: "0016"
        variabiliTecniche:
          type: array
          description: Coverage-specific technical variables (caps).
          items:
            $ref: "#/components/schemas/VariabileTecnicaGaranzia"
        premioGaranzia:
          $ref: "#/components/schemas/PremioGaranzia"

    # ── Variabile Tecnica Garanzia ────────────────────────────────────────────
    VariabileTecnicaGaranzia:
      type: object
      description: |
        Coverage-specific technical variable (caps) for `rischioVolo`.

        | `keyVartec`          | Type   | BASE tier                                     | TOP tier |
        |:----------------------|:------|:------------------------------------------------|:------------------|
        | `MASSIMALE`          | number | `40000` (same value for Italia / Europa / Mondo) | `40000` (same for all areas) |
        | `MASSIMALE_PERSONA`  | number | `500` for Italia/Europa, `1000` for Mondo — the value matching the enrolment's `destinazioneArea` | `2500` (all areas) |

        There is no explicit tier selector field: the partner's pricing engine already resolves
        the BASE or TOP configuration for the end customer, and sends the corresponding numeric
        cap values directly.
      required:
        - keyVartec
        - valueVartec
      properties:
        idGaranzia:
          type: string
          format: uuid
          description: |
            Reference to the id of the parent coverage. **Always overwritten/rebuilt by the
            backend** — may be omitted or sent as a placeholder.
        keyVartec:
          type: string
          description: |
            Technical variable key of the coverage (UPPER_CASE, case-sensitive).
            Valid values for `rischioVolo`: `MASSIMALE`, `MASSIMALE_PERSONA`.
          enum:
            - MASSIMALE
            - MASSIMALE_PERSONA
          example: "MASSIMALE"
        valueVartec:
          description: Value of the technical variable. Can be numeric or string.
          oneOf:
            - type: number
            - type: string
          example: 40000

    # ── Premio Garanzia ───────────────────────────────────────────────────────
    PremioGaranzia:
      type: object
      description: Net premium, taxes and gross premium of the coverage.
      required:
        - id
        - impAnnuo
        - tassePerc
        - lordoAnnuo
        - valuta
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier of the premium (UUID v4).
        impAnnuo:
          type: number
          description: Annual net premium (dot as decimal separator).
          example: 11.56
        tassePerc:
          type: number
          description: |
            Tax percentage applied to the premium.
            ** Provisional value — a breakdown across multiple tax rates is expected; do not
            treat 21.25% as final.
          example: 21.25
        lordoAnnuo:
          type: number
          description: Annual gross premium (net + taxes).
          example: 21.25
        valuta:
          type: string
          description: Currency code (ISO 4217).
          example: "EUR"
        impAnnuoEur:
          type: number
          description: Annual net premium in EUR (same as `impAnnuo` if currency is EUR).
          example: 10.72
        lordoAnnuoEur:
          type: number
          description: Annual gross premium in EUR (same as `lordoAnnuo` if currency is EUR).
          example: 13.00

    # ── Storno Request ────────────────────────────────────────────────────────
    StornoRequest:
      type: object
      description: Payload to cancel (storno) an existing enrolment.
      required:
        - numeroAdesione
        - frazionamento
        - tipoMovimento
        - dataMovimento
      properties:
        numeroAdesione:
          type: string
          description: Unique identifier of the enrolment to cancel.
          example: "OX00232638-000003"
        frazionamento:
          type: string
          description: Payment periodicity associated with the enrolment.
          example: "TRIMESTRALE"
        tipoMovimento:
          type: string
          description: Type of administrative movement.
          enum:
            - ANNULLO_SENZA_EFFETTO
            - ANNULLO_CON_RIMBORSO
          example: "ANNULLO_SENZA_EFFETTO"
        dataMovimento:
          type: string
          description: Movement date (ISO — `yyyy-MM-dd` or full ISO 8601).
          example: "2026-04-24T00:00:00.000Z"

    ErrorResponse:
      type: object
      description: Standard error response.
      properties:
        code:
          type: string
          description: Internal error code.
        message:
          type: string
          description: Human-readable error description.
        details:
          type: array
          description: List of additional error details (invalid field, violated constraint, etc.).
          items:
            type: object
            properties:
              field:
                type: string
                description: Name of the field that caused the error.
              reason:
                type: string
                description: Reason for the error on the field.

  # ── Examples ──────────────────────────────────────────────────────────────
  examples:
    AdesioneSample:
      summary: Sample enrolment I4Move — rischioVolo, BASE tier, 1 insured, domestic destination
      value:
        keyPolizza: "749115fa-a5c9-4c98-9461-f10b7a6ae559"
        numeroAdesioneEsterno: "ADH-2026-00123"
        numeroPolizza: "OXC0XXX"
        dataCreazione: "2026-04-10T12:22:27.871Z"
        dataEffetto: "2026-04-24T00:00:00.000Z"
        dataScadenza: "2026-04-30T00:00:00.000Z"
        tacitoRinnovo: false
        source: "FE"
        stato: "EMESSA"
        codiceProdotto: "A001"
        tipoSoggetto: "PERSONA_FISICA"
        personaFisica:
          codiceFiscale: "BNCMRC85M10L781A"
          nome: "Marco"
          cognome: "Bianchi"
          nazione: "ESP"
          indirizzo: "Calle Mayor 15, 28013 Madrid"
          numeroTelefono: 612345678
          email: "marco.bianchi@email.it"
          pec: ""
        beneAssicurato:
          tipoBene: "gruppo di assicurati"
          identificativo: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
          variabiliTecniche:
            - id: "4f3e9dde-f001-4b9c-9757-e7c2a9bbf7e2"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "prenotazione"
              valueVartec: "2026-04-10T12:00:00+02:00"
            - id: "8fa94a3d-2162-4158-a2a1-d3a981b15b89"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "destinazioneArea"
              valueVartec: "italia"
            - id: "492a5a91-8012-4e87-b0e5-693aae393b31"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "dataPartenza"
              valueVartec: "2026-04-24T12:00:00+02:00"
            - id: "757d6edb-4cc6-432c-a883-490943f91dc5"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "destinazionePaese"
              valueVartec: "Italia"
            - id: "e7d59bc6-0308-4afb-8138-046632a9a684"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "dataRientro"
              valueVartec: "2026-04-30T12:00:00+02:00"
            - id: "c0358e63-04d6-41b1-a1ac-612e46acd5ab"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "dataGridDatiAssicurato"
              valueVartec:
                - cognomeAssicurato: "Bianchi"
                  nomeAssicurato: "Marco"
                  fasciaEta: "media"
                  email: "marco.bianchi@email.it"
            - id: "b9514356-6cba-4fd8-97b4-1a834d113def"
              idBeneAssicurato: "9f5bad19-3f0c-4252-a9b7-90a7a449196a"
              keyVartec: "numeroAssicurati"
              valueVartec: 1
        garanzie:
          - id: "2efe35f3-9b1b-49b3-a0a3-c595f4654199"
            idGarOverx: "5f09a062-e355-48cb-8232-c6fc7dd88973"
            nomeGaranzia: "RISCHIO VOLO"
            keyGaranzia: "rischioVolo"
            codAnagGar: "16032"
            ramoBilancio: "0016"
            variabiliTecniche:
              - idGaranzia: "2efe35f3-9b1b-49b3-a0a3-c595f4654199"
                keyVartec: "MASSIMALE"
                valueVartec: 40000
              - idGaranzia: "2efe35f3-9b1b-49b3-a0a3-c595f4654199"
                keyVartec: "MASSIMALE_PERSONA"
                valueVartec: 500
            premioGaranzia:
              id: "54a048d2-0dff-4281-99c9-2c8c4afb08e8"
              impAnnuo: 10.72
              tassePerc: 21.25 # ** provisional, see tassePerc description
              lordoAnnuo: 13.00
              valuta: "EUR"
              impAnnuoEur: 10.72
              lordoAnnuoEur: 13.00
