openapi: 3.0.0
paths:
  /api/v1/me:
    get:
      description: Resolves the display name, first name, and email of the caller identified by the platform guard (JWT or the legacy CM SSO header). Degrades to null fields on a CM outage or unknown person.
      operationId: CurrentUserController_getCurrentUser_v1
      parameters: []
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CurrentUserResponseDto"
      security:
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get the signed-in CM user
      tags:
        - Current user
  /api/v1/organisations/me:
    get:
      description: Returns the organisation resolved from the request tenant. The caller must have CM access to the logical account; access is validated by the platform guard before this handler runs.
      operationId: OrganisationsController_getCurrentOrganisation_v1
      parameters: []
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrganisationResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get the current organisation
      tags:
        - Organisations
  /api/v1/overview/stats:
    get:
      description: Aggregate membership stats for the tenant resolved from the request.
      operationId: OverviewController_getStats_v1
      parameters: []
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OverviewStatsResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get management overview stats
      tags:
        - Overview
  /api/v1/parties:
    post:
      description: Creates a party scoped to the tenant resolved from the request.
      operationId: PartiesController_create_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartySchema"
            examples:
              naturalPerson:
                summary: Natural person
                value:
                  type: NATURAL_PERSON
                  givenName: Anna
                  familyName: de Vries
                  dateOfBirth: 1990-05-14
                  email: anna.devries@example.com
                  phonePrimary: "+31612345678"
              organisation:
                summary: Organisation
                value:
                  type: ORGANISATION
                  legalName: CM.com B.V.
                  email: billing@cm.com
                  phonePrimary: "+31765451818"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a party
      tags:
        - Parties
    get:
      description: Lists parties scoped to the tenant resolved from the request, optionally filtered by type, role and a free-text search term.
      operationId: PartiesController_findAll_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: searchTerm
          required: false
          in: query
          description: Free-text filter matched (case-insensitively) against given name, family name, full name, legal name, email, phone numbers, the start of the postal code of any of their addresses (spaces and case ignored), and the membership number of memberships the party belongs to or owns.
          schema:
            example: Ada
            type: string
        - name: role
          required: false
          in: query
          description: Filter results to parties holding this link role; omit to list every party. `OWNER` always returns an empty result for now, since that role has no backing data yet.
          schema:
            enum:
              - OWNER
              - MEMBER
            type: string
        - name: type
          required: false
          in: query
          description: Filter results by party type.
          schema:
            enum:
              - NATURAL_PERSON
              - ORGANISATION
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PartyResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List parties
      tags:
        - Parties
  /api/v1/parties/search:
    get:
      description: Searches parties by name, email, postal code or membership number across the entire tenant, scoped to the organisation resolved from the request. Ignores the current view's role/type filters and always searches all parties, returning at most 10 results.
      operationId: PartiesController_search_v1
      parameters:
        - name: searchTerm
          required: true
          in: query
          description: Search term matched (case-insensitively) against given name, family name, full name, legal name, email, the start of the postal code of any of their addresses (spaces and case ignored), and the membership number of memberships the party belongs to or owns.
          schema:
            example: Ada
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PartyResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Search parties
      tags:
        - Parties
  /api/v1/parties/export:
    get:
      description: Streams an .xlsx workbook of the tenant's parties. The Relations worksheet is always included; `withAddresses` and `withCards` add an Addresses and a Memberships worksheet respectively.
      operationId: PartiesController_export_v1
      parameters:
        - name: language
          required: false
          in: query
          description: Language for translated names; defaults to the organisation language.
          schema:
            type: string
            enum:
              - DE
              - EN
              - ES
              - FR
              - HE
              - NL
        - name: ids
          required: false
          in: query
          description: Relation ids to scope the export to; omit to export the whole tenant.
          schema:
            type: array
            items:
              type: string
        - name: withCards
          required: false
          in: query
          description: Include a Memberships worksheet.
          schema:
            type: boolean
        - name: withAddresses
          required: false
          in: query
          description: Include an Addresses worksheet.
          schema:
            type: boolean
      responses:
        "200":
          description: ""
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Export parties
      tags:
        - Parties
  /api/v1/parties/{id}:
    get:
      description: Fetches a single party by its public id, scoped to the tenant resolved from the request. Returns 404 if the party does not exist or belongs to a different organisation.
      operationId: PartiesController_getById_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a party by id
      tags:
        - Parties
    patch:
      description: Partially updates a party, scoped to the tenant resolved from the request. Only fields present in the request body are changed. Returns 404 if the party does not exist or belongs to a different organisation.
      operationId: PartiesController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePartyDto"
            examples:
              updateContactDetails:
                summary: Update contact details
                value:
                  email: anna.devries@example.com
                  phonePrimary: "+31687654321"
              correctLegalName:
                summary: Correct legal name
                value:
                  legalName: CM Netherlands B.V.
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a party
      tags:
        - Parties
  /api/v1/parties/{id}/addresses:
    post:
      description: Adds a party's first address of a kind, scoped to the tenant resolved from the request. Once a current (or pending) address of that kind exists, use `POST /addresses/move` instead - this returns 409 if one already does. Returns 404 if the party does not exist or belongs to a different organisation.
      operationId: AddressesController_create_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAddressSchema"
            examples:
              homeAddress:
                summary: Home address (NL)
                value:
                  kind: POSTAL
                  street: Stationsplein
                  houseNumber: "9"
                  houseNumberSuffix: K
                  postalCode: 4811 BM
                  city: Breda
                  countryCode: NL
              businessAddress:
                summary: Billing address (BE)
                value:
                  kind: BILLING
                  attentionLine: Finance Department
                  street: Boulevard Anspachlaan
                  houseNumber: "1"
                  postalCode: "1000"
                  city: Brussel
                  countryCode: BE
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AddressResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Add the first address of a kind to a party
      tags:
        - Parties
    get:
      description: Lists all of a party's addresses (current, previous and pending), scoped to the tenant resolved from the request. Returns 404 if the party does not exist or belongs to a different organisation.
      operationId: AddressesController_findAll_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/AddressResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List a party's addresses
      tags:
        - Parties
  /api/v1/parties/{id}/addresses/{addressId}:
    patch:
      description: Replaces every field of one of a party's addresses except `kind`/`validFrom`/`validTo` - this is a correction of the address itself, not a house move (use `POST /addresses/move` for that). Returns 404 if the party or the address does not exist, or the address does not belong to the party.
      operationId: AddressesController_correct_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: addressId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CorrectAddressSchema"
            examples:
              correctPostalCode:
                summary: Correct a typo in the postal code
                value:
                  street: Stationsplein
                  houseNumber: "9"
                  houseNumberSuffix: K
                  postalCode: 4811 BN
                  city: Breda
                  countryCode: NL
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AddressResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Correct a party address
      tags:
        - Parties
    delete:
      description: Cancels a pending (strictly future-dated) address move by deleting that row outright, scoped to the tenant resolved from the request. Returns 404 if the party or the address does not exist, or the address does not belong to the party; returns 409 if the address is not a pending move (i.e. its `validFrom` is today or in the past).
      operationId: AddressesController_cancelPendingMove_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: addressId
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Cancel a pending address move
      tags:
        - Parties
  /api/v1/parties/{id}/addresses/move:
    post:
      description: Closes a party's current address of a kind the day before `effectiveDate` and adds the new one from that date, scoped to the tenant resolved from the request. Returns 404 if the party does not exist or belongs to a different organisation, or has no current address of that kind yet (use `POST /addresses` for the first one); returns 409 if `effectiveDate` overlaps an existing address of that kind (e.g. another pending move).
      operationId: AddressesController_move_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MoveAddressSchema"
            examples:
              moveToNewAddress:
                summary: Move to a new address next month
                value:
                  street: Prinsengracht
                  houseNumber: "263"
                  postalCode: 1016 GV
                  city: Amsterdam
                  countryCode: NL
                  effectiveDate: 2026-09-01
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AddressResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Move a party to a new address
      tags:
        - Parties
  /api/v1/parties/{id}/notes:
    post:
      description: Appends a free-text note to a party, scoped to the tenant resolved from the request. Returns 404 if the party does not exist or belongs to a different organisation.
      operationId: NotesController_create_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NoteSchema"
            examples:
              addNote:
                summary: Add a note
                value:
                  text: Called the customer, they confirmed the new address.
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NoteResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Add a note to a party
      tags:
        - Notes
    get:
      description: Lists notes for a party, scoped to the tenant resolved from the request. Returns 404 if the party does not exist or belongs to a different organisation.
      operationId: NotesController_findAll_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/NoteResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List notes for a party
      tags:
        - Notes
  /api/v1/membership-types:
    post:
      description: Creates a membership type scoped to the tenant resolved from the request.
      operationId: MembershipTypesController_create_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateMembershipTypeSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipTypeResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a membership type
      tags:
        - Membership Types
    get:
      description: Lists all membership types belonging to the tenant resolved from the request.
      operationId: MembershipTypesController_list_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/MembershipTypeResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List membership types
      tags:
        - Membership Types
  /api/v1/membership-types/{id}:
    get:
      description: Fetches a single membership type, scoped to the tenant resolved from the request.
      operationId: MembershipTypesController_getOne_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipTypeResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a membership type
      tags:
        - Membership Types
    patch:
      description: Updates a single membership type, scoped to the tenant resolved from the request.
      operationId: MembershipTypesController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateMembershipTypeSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipTypeResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a membership type
      tags:
        - Membership Types
    delete:
      description: Soft-deletes a single membership type, scoped to the tenant resolved from the request.
      operationId: MembershipTypesController_delete_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Delete a membership type
      tags:
        - Membership Types
  /api/v1/vat-rates:
    get:
      description: Lists the statutory VAT rates, optionally filtered to a single country.
      operationId: VatRatesController_list_v1
      parameters:
        - name: countryCode
          required: false
          in: query
          description: Filter by ISO 3166-1 alpha-2 country code. Case-insensitive. Omit to list every country.
          schema:
            minLength: 2
            maxLength: 2
            example: NL
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/VatRateResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List VAT rates
      tags:
        - VAT Rates
  /api/v1/settings:
    get:
      description: Returns the effective settings for the tenant; secrets report only whether they are set.
      operationId: SettingsController_get_v1
      parameters: []
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SettingsResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get settings
      tags:
        - Settings
    put:
      description: Validates and stores the given settings items for the tenant; secrets are encrypted at rest.
      operationId: SettingsController_upsert_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertSettingSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SettingsResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Upsert settings
      tags:
        - Settings
  /api/v1/custom-field-definitions:
    post:
      description: Creates a custom field definition scoped to the tenant resolved from the request.
      operationId: CustomFieldDefinitionsController_create_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCustomFieldDefinitionSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomFieldDefinitionResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a custom field definition
      tags:
        - Custom Field Definitions
    get:
      description: Lists custom field definitions for the tenant, optionally filtered by scope.
      operationId: CustomFieldDefinitionsController_list_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: scope
          required: false
          in: query
          description: Restrict to a single scope.
          schema:
            type: string
            enum:
              - PARTY
              - MEMBERSHIP
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/CustomFieldDefinitionResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List custom field definitions
      tags:
        - Custom Field Definitions
  /api/v1/custom-field-definitions/{id}:
    get:
      description: Fetches a single custom field definition, scoped to the tenant resolved from the request.
      operationId: CustomFieldDefinitionsController_getOne_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomFieldDefinitionResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a custom field definition
      tags:
        - Custom Field Definitions
    patch:
      description: Updates a custom field definition; kind, scope and key are immutable.
      operationId: CustomFieldDefinitionsController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateCustomFieldDefinitionSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomFieldDefinitionResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a custom field definition
      tags:
        - Custom Field Definitions
    delete:
      description: Soft-deletes a custom field definition, scoped to the tenant resolved from the request.
      operationId: CustomFieldDefinitionsController_delete_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Delete a custom field definition
      tags:
        - Custom Field Definitions
  /api/v1/custom-field-values:
    get:
      description: Returns every definition in the given scope paired with its stored value for the target entity (null when none is stored yet).
      operationId: CustomFieldValuesController_get_v1
      parameters:
        - name: scope
          required: true
          in: query
          description: Which definition set the values belong to.
          schema:
            type: string
            enum:
              - PARTY
              - MEMBERSHIP
        - name: entityId
          required: true
          in: query
          description: Opaque ulid of the target entity the values attach to.
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomFieldValuesResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get custom field values for an entity
      tags:
        - Custom Field Values
    put:
      description: Stores the given custom field values for the target entity, scoped to the tenant.
      operationId: CustomFieldValuesController_upsert_v1
      parameters:
        - name: scope
          required: true
          in: query
          description: Which definition set the values belong to.
          schema:
            type: string
            enum:
              - PARTY
              - MEMBERSHIP
        - name: entityId
          required: true
          in: query
          description: Opaque ulid of the target entity the values attach to.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertCustomFieldValuesSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomFieldValuesResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Upsert custom field values for an entity
      tags:
        - Custom Field Values
  /api/v1/message-templates:
    get:
      description: Lists the lifecycle message templates for the tenant resolved from the request, together with the sender name (the sender name setting, falling back to the organisation name).
      operationId: MessageTemplatesController_list_v1
      parameters: []
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplateListResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List message templates
      tags:
        - Message Templates
  /api/v1/message-templates/{type}:
    put:
      description: Updates a single lifecycle message template, scoped to the tenant resolved from the request.
      operationId: MessageTemplatesController_update_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateMessageTemplateSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplateResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a message template
      tags:
        - Message Templates
  /api/v1/message-templates/preview:
    post:
      description: Synchronously renders the current, possibly-unsaved form field values through mail-broker and returns the resulting HTML.
      operationId: MessageTemplatesController_preview_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PreviewMessageTemplateSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageTemplatePreviewResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Preview a message template
      tags:
        - Message Templates
  /api/v1/consent-definitions:
    post:
      description: Creates a consent definition scoped to the tenant resolved from the request.
      operationId: ConsentDefinitionsController_create_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateConsentDefinitionSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsentDefinitionResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a consent definition
      tags:
        - Consent Definitions
    get:
      description: Lists consent definitions for the tenant, in display order.
      operationId: ConsentDefinitionsController_list_v1
      parameters: []
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsentDefinitionsResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List consent definitions
      tags:
        - Consent Definitions
  /api/v1/consent-definitions/{id}:
    get:
      description: Fetches a single consent definition, scoped to the tenant resolved from the request.
      operationId: ConsentDefinitionsController_getOne_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsentDefinitionResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a consent definition
      tags:
        - Consent Definitions
    patch:
      description: Updates a consent definition; the key is immutable. Editing the legal text bumps the version.
      operationId: ConsentDefinitionsController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateConsentDefinitionSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsentDefinitionResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a consent definition
      tags:
        - Consent Definitions
    delete:
      description: Soft-deletes a consent definition, scoped to the tenant resolved from the request.
      operationId: ConsentDefinitionsController_delete_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Delete a consent definition
      tags:
        - Consent Definitions
  /api/v1/benefits:
    post:
      description: Creates a benefit in the tenant benefit library.
      operationId: BenefitsController_create_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateBenefitSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a benefit
      tags:
        - Benefits
    get:
      description: Lists the tenant benefit library, active benefits first.
      operationId: BenefitsController_list_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: statuses
          required: false
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - ACTIVE
                - INACTIVE
        - name: types
          required: false
          in: query
          schema:
            type: array
            items:
              type: string
              enum:
                - ACCESS
                - PRODUCT_DISCOUNT
        - name: notLinkedToMembershipTypeId
          required: false
          in: query
          description: Membership type id; leaves out the benefits already linked to it.
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/BenefitResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List benefits
      tags:
        - Benefits
  /api/v1/benefits/{id}:
    get:
      description: Fetches a single benefit.
      operationId: BenefitsController_getOne_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitDetailResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a benefit
      tags:
        - Benefits
    patch:
      description: Updates a benefit; its type cannot change.
      operationId: BenefitsController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateBenefitSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a benefit
      tags:
        - Benefits
    delete:
      description: Soft-deletes a benefit.
      operationId: BenefitsController_delete_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Delete a benefit
      tags:
        - Benefits
  /api/v1/membership-types/{membershipTypeId}/benefits:
    get:
      description: Lists the benefits linked to the membership type.
      operationId: MembershipTypeBenefitsController_list_v1
      parameters:
        - name: membershipTypeId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/BenefitResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List linked benefits
      tags:
        - Benefits
    post:
      description: Links an active benefit to the membership type.
      operationId: MembershipTypeBenefitsController_link_v1
      parameters:
        - name: membershipTypeId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LinkBenefitSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Link a benefit
      tags:
        - Benefits
  /api/v1/membership-types/{membershipTypeId}/benefits/{benefitId}:
    delete:
      description: Removes the link; the benefit stays in the library.
      operationId: MembershipTypeBenefitsController_unlink_v1
      parameters:
        - name: membershipTypeId
          required: true
          in: path
          schema:
            type: string
        - name: benefitId
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Unlink a benefit
      tags:
        - Benefits
  /api/v1/benefit-usage:
    get:
      description: Lists the benefit uses of one member or of a whole membership; a reversed use carries `reversedAt`.
      operationId: BenefitUsageController_list_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: membershipNumber
          required: false
          in: query
          description: One member.
          schema:
            example: "2630346069"
            type: string
        - name: membershipId
          required: false
          in: query
          description: "Membership id: every member of the household."
          schema:
            type: string
        - name: benefitKey
          required: false
          in: query
          schema:
            example: benefit.lounge_access
            type: string
        - name: type
          required: false
          in: query
          schema:
            type: string
            enum:
              - ACCESS
              - PRODUCT_DISCOUNT
        - name: results
          required: false
          in: query
          description: Leave out to include every result.
          schema:
            type: array
            items:
              type: string
              enum:
                - ALLOWED
                - MEMBERSHIP_NOT_FOUND
                - MEMBERSHIP_NOT_ENTITLED
                - MEMBERSHIP_BLOCKED
                - NOT_APPLICABLE
                - DENIED_BY_RULE
                - LIMIT_REACHED
        - name: from
          required: false
          in: query
          description: First business date, inclusive.
          schema:
            format: date
            type: string
        - name: to
          required: false
          in: query
          description: Last business date, inclusive.
          schema:
            format: date
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/BenefitUsageListItemDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List benefit usage
      tags:
        - Benefit usage
    post:
      description: Checks and records a use atomically. Without `benefitKey` the best applicable benefit of the type is used.
      operationId: BenefitUsageController_redeem_v1
      parameters:
        - name: Idempotency-Key
          in: header
          description: Caller-generated key; a retry with the same key and payload returns the original outcome, a refusal included. Use a new key to try again.
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RedeemBenefitSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitUsageResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Redeem a benefit
      tags:
        - Benefit usage
  /api/v1/benefit-usage/validate:
    post:
      description: Answers, for one benefit type, whether the member may use it right now and which benefits apply. Has no side effects.
      operationId: BenefitUsageController_validate_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ValidateBenefitSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitValidationResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Check a member's benefits
      tags:
        - Benefit usage
  /api/v1/benefit-usage/{usageId}/reverse:
    post:
      description: Gives the use back so it no longer counts toward the limit; reversing twice returns the same use.
      operationId: BenefitUsageController_reverse_v1
      parameters:
        - name: usageId
          required: true
          in: path
          schema:
            type: string
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BenefitUsageResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Reverse a use
      tags:
        - Benefit usage
  /api/v1/consent-records:
    get:
      description: Returns every consent definition paired with the party's current state (the latest ledger record), including whether a granted consent is outdated.
      operationId: ConsentRecordsController_get_v1
      parameters:
        - name: partyId
          required: true
          in: query
          description: Opaque ulid of the party (data subject) whose consent state is requested.
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyConsentStateResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a party's consent state
      tags:
        - Consent Records
    post:
      description: Appends grant/withdraw records for the given party. Each decision is an immutable event; the latest per definition is the current state. Mandatory consents are not enforced here.
      operationId: ConsentRecordsController_record_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordConsentGrantsSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyConsentStateResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Record consent decisions for a party
      tags:
        - Consent Records
  /api/v1/price-plans/{id}:
    get:
      description: Fetches a single price plan, scoped to the tenant resolved from the request.
      operationId: PricePlansController_getOne_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PricePlanResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a price plan
      tags:
        - Price Plans
    patch:
      description: "Changes a price plan's name, active state and, optionally, the end of its membership period. A new end date must be after the start date and in the future; null drops it and makes the plan open-ended. The start date and the billing cycle are fixed: changing either would rewrite the commercial terms of every membership already on the plan."
      operationId: PricePlansController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePricePlanSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PricePlanResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Update a price plan
      tags:
        - Price Plans
  /api/v1/membership-types/{membershipTypeId}/price-plans:
    post:
      description: Creates a price plan under the given membership type. The membership period starts on `startDate`; supply an `endDate` for a fixed-term period, or omit it for an open-ended plan that does not end.
      operationId: MembershipTypePricePlansController_create_v1
      parameters:
        - name: membershipTypeId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePricePlanSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PricePlanResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a price plan
      tags:
        - Price Plans
    get:
      description: Lists the price plans belonging to the given membership type.
      operationId: MembershipTypePricePlansController_list_v1
      parameters:
        - name: membershipTypeId
          required: true
          in: path
          schema:
            type: string
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PricePlanResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List price plans
      tags:
        - Price Plans
  /api/v1/prices/{id}:
    patch:
      description: A price that has not taken effect yet accepts every field. Once its `validFrom` has passed only `renewalPrice` may still change — attempting anything else returns 422.
      operationId: PricesController_update_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePriceSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PriceResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Change a price
      tags:
        - Prices
    delete:
      description: Only a price that has not taken effect yet can be deleted. One that has is part of the timeline and is superseded by a later price instead, which returns 422.
      operationId: PricesController_delete_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Delete a price
      tags:
        - Prices
  /api/v1/price-plans/{pricePlanId}/prices:
    post:
      description: Records a new price effective from `validFrom`, which applies until the next price on the plan starts. No other price on the plan may start on the same day. The currency is taken from the organisation and cannot be supplied.
      operationId: PricePlanPricesController_create_v1
      parameters:
        - name: pricePlanId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePriceSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PriceResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Add a price to a price plan
      tags:
        - Prices
    get:
      description: Lists the price timeline of the given price plan, oldest first.
      operationId: PricePlanPricesController_list_v1
      parameters:
        - name: pricePlanId
          required: true
          in: path
          schema:
            type: string
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PriceResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List the prices of a price plan
      tags:
        - Prices
  /api/v1/integrations/mail-broker/test-emails:
    post:
      description: Publishes a test email via the mail-broker Pub/Sub topic, addressed to the current user.
      operationId: IntegrationsController_sendTestEmail_v1
      parameters: []
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Send a test email
      tags:
        - Integrations
  /api/v1/memberships:
    post:
      description: Creates a membership on the given membership type and price plan, with the given party as its owner.
      operationId: MembershipsController_create_v1
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateMembershipSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create a membership
      tags:
        - Memberships
    get:
      description: Lists memberships belonging to the tenant resolved from the request, optionally filtered by status, membership type, price plan, owner and start-date period.
      operationId: MembershipsController_list_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: statuses
          required: false
          in: query
          description: Filter results by lifecycle status. Repeatable; results match any of the given statuses.
          schema:
            type: array
            items:
              type: string
              enum:
                - DRAFT
                - ACTIVE
                - CANCELLED
                - EXPIRED
        - name: membershipTypeIds
          required: false
          in: query
          description: Filter results by the opaque external identifier of the membership type. Repeatable; results match any of the given ids.
          schema:
            type: array
            items: {}
        - name: pricePlanIds
          required: false
          in: query
          description: Filter results by the opaque external identifier of the price plan. Repeatable; results match any of the given ids.
          schema:
            type: array
            items: {}
        - name: ownerSearch
          required: false
          in: query
          description: Filter results by a case-insensitive match on the owner party's name or email.
          schema: {}
        - name: searchTerm
          required: false
          in: query
          description: Filter results by a case-insensitive match on the name or email of the owner or of a linked member, or on the membership number.
          schema: {}
        - name: ownerPartyId
          required: false
          in: query
          description: Filter results to memberships owned by the given party (opaque external identifier).
          schema: {}
        - name: memberPartyId
          required: false
          in: query
          description: Filter results to memberships the given party is linked to as a member (opaque external identifier).
          schema: {}
        - name: periodFrom
          required: false
          in: query
          description: Only include memberships whose start date is on or after this calendar day.
          schema:
            example: 2026-01-01
        - name: periodTo
          required: false
          in: query
          description: Only include memberships whose start date is on or before this calendar day.
          schema:
            example: 2026-12-31
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/MembershipResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List memberships
      tags:
        - Memberships
  /api/v1/memberships/export:
    get:
      description: Streams an .xlsx workbook of the tenant's memberships, honouring the same filters as the list. `ids` scopes the export to a selected subset; omit it to export every filtered membership.
      operationId: MembershipsController_export_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: language
          required: false
          in: query
          description: Language for translated names; defaults to the organisation language.
          schema:
            type: string
            enum:
              - DE
              - EN
              - ES
              - FR
              - HE
              - NL
        - name: ids
          required: false
          in: query
          description: Membership ids to scope the export to; omit to export every filtered membership.
          schema:
            type: array
            items: {}
        - name: statuses
          required: false
          in: query
          description: Filter results by lifecycle status. Repeatable; results match any of the given statuses.
          schema:
            type: array
            items:
              type: string
              enum:
                - DRAFT
                - ACTIVE
                - CANCELLED
                - EXPIRED
        - name: membershipTypeIds
          required: false
          in: query
          description: Filter results by the opaque external identifier of the membership type. Repeatable; results match any of the given ids.
          schema:
            type: array
            items: {}
        - name: pricePlanIds
          required: false
          in: query
          description: Filter results by the opaque external identifier of the price plan. Repeatable; results match any of the given ids.
          schema:
            type: array
            items: {}
        - name: ownerSearch
          required: false
          in: query
          description: Filter results by a case-insensitive match on the owner party's name or email.
          schema: {}
        - name: searchTerm
          required: false
          in: query
          description: Filter results by a case-insensitive match on the name or email of the owner or of a linked member, or on the membership number.
          schema: {}
        - name: ownerPartyId
          required: false
          in: query
          description: Filter results to memberships owned by the given party (opaque external identifier).
          schema: {}
        - name: memberPartyId
          required: false
          in: query
          description: Filter results to memberships the given party is linked to as a member (opaque external identifier).
          schema: {}
        - name: periodFrom
          required: false
          in: query
          description: Only include memberships whose start date is on or after this calendar day.
          schema:
            example: 2026-01-01
        - name: periodTo
          required: false
          in: query
          description: Only include memberships whose start date is on or before this calendar day.
          schema:
            example: 2026-12-31
      responses:
        "200":
          description: ""
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Export memberships
      tags:
        - Memberships
  /api/v1/memberships/{id}:
    get:
      description: Fetches a single membership, scoped to the tenant resolved from the request.
      operationId: MembershipsController_getOne_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a membership
      tags:
        - Memberships
  /api/v1/memberships/{id}/cancellation:
    post:
      description: Cancels the membership and records the given calendar day as its end date.
      operationId: MembershipsController_cancel_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CancelMembershipSchema"
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Cancel a membership
      tags:
        - Memberships
  /api/v1/memberships/{id}/reactivation:
    post:
      description: Reactivates a cancelled membership immediately, clearing its end date; the billing cycle continues unchanged.
      operationId: MembershipsController_reactivate_v1
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Reactivate a membership
      tags:
        - Memberships
  /api/v1/parties/{partyId}/memberships:
    get:
      description: Lists the memberships the given party is a member of.
      operationId: PartyMembershipsController_list_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PartyMembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List a party's memberships
      tags:
        - Party Memberships
  /api/v1/memberships/{membershipId}/parties:
    post:
      description: Links a party to the membership as a member. The owner lives on the membership itself (set when it is created), not through this endpoint.
      operationId: MembershipPartiesController_create_v1
      parameters:
        - name: membershipId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePartyMembershipSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyMembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Add a member to a membership
      tags:
        - Party Memberships
    get:
      description: Lists every member linked to the membership; the owner lives on the membership itself.
      operationId: MembershipPartiesController_list_v1
      parameters:
        - name: membershipId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/MembershipMemberResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List a membership's members
      tags:
        - Party Memberships
  /api/v1/memberships/{membershipId}/parties/{partyId}/membership-number:
    post:
      description: Regenerates the membership number for a linked party — used when a card is lost or on suspected fraud. The previous number stops working immediately.
      operationId: MembershipPartiesController_regenerateMembershipNumber_v1
      parameters:
        - name: membershipId
          required: true
          in: path
          schema:
            type: string
        - name: partyId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyMembershipResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Issue a new membership number
      tags:
        - Party Memberships
  /api/v1/memberships/{membershipId}/parties/{partyId}:
    delete:
      description: Unlinks a member. The owner is not a link here and cannot be reached through this endpoint.
      operationId: MembershipPartiesController_delete_v1
      parameters:
        - name: membershipId
          required: true
          in: path
          schema:
            type: string
        - name: partyId
          required: true
          in: path
          schema:
            type: string
      responses:
        "204":
          description: ""
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Remove a member from a membership
      tags:
        - Party Memberships
  /api/v1/parties/{partyId}/mandate:
    get:
      description: Fetches the party's most recently issued mandate, active or revoked, or null when it has none.
      operationId: PartyMandatesController_get_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: The party's most recent mandate — check `revokedAt` for whether it's still usable — or null when it has none.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PartyMandateResponseDto"
                nullable: true
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a party's mandate
      tags:
        - Party mandates
    post:
      description: Records a mandate a tenant collected directly from the party (e.g. at the counter, by phone or by email) rather than through a PSP flow. Revokes any previously active mandate for the party before creating the new one.
      operationId: PartyMandatesController_createManual_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateManualPartyMandateSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartyMandateResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Create or renew a party's manual SEPA Direct Debit mandate
      tags:
        - Party mandates
  /api/v1/parties/{partyId}/mandate/authorisation:
    post:
      description: Opens a two-cent order at the payment provider and returns the provider's hosted payment page, where the party authorises the new mandate. The order reuses the payment provider subscription of the party's latest mandate, active or revoked; a party without one is first set up at the payment provider, which requires an address and an email. The mandate is created once the payment provider reports the payment; any mandate the party has stays in place until then.
      operationId: PartyMandatesController_authorise_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
      responses:
        "202":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MandateAuthorisationResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Start a party's authorisation of a new mandate
      tags:
        - Party mandates
  /api/v1/parties/{partyId}/invoices:
    get:
      description: Lists the invoices due on memberships the given party owns or is a member of, most recently due first by default.
      operationId: PartyInvoicesController_list_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
        - name: order
          required: false
          in: query
          description: Sort order applied to the list.
          schema:
            default: ASC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/InvoiceResponseDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List a party's invoices
      tags:
        - Party Invoices
  /api/v1/parties/{partyId}/invoices/{invoiceId}:
    get:
      description: The attempt history, mandate used, and dunning status for a single invoice.
      operationId: PartyInvoicesController_getOne_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
        - name: invoiceId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceDetailResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Get a party's invoice detail
      tags:
        - Party Invoices
  /api/v1/parties/{partyId}/invoices/{invoiceId}/attempts:
    post:
      description: Registers a payment attempt that already happened outside the gateway pipeline (cash, bank transfer, correction), marking an existing invoice paid. Never creates a new invoice or changes its amount.
      operationId: PartyInvoicesController_registerAttempt_v1
      parameters:
        - name: partyId
          required: true
          in: path
          schema:
            type: string
        - name: invoiceId
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterPaymentAttemptDto"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceDetailResponseDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Manually register a payment attempt
      tags:
        - Party Invoices
  /api/v1/invoices:
    get:
      description: Lists the organisation's invoices, most recently due first by default.
      operationId: InvoicesController_list_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order on due date applied to the list.
          schema:
            default: DESC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: statuses
          required: false
          in: query
          description: Results match any of the given statuses.
          schema:
            type: array
            items:
              type: string
              enum:
                - PENDING
                - PROCESSING
                - SUCCEEDED
                - FAILED_RETRYABLE
                - REQUIRES_CUSTOMER_ACTION
                - PENDING_AT_PSP
                - FAILED_FINAL
                - REVERSED
        - name: membershipTypeIds
          required: false
          in: query
          description: Opaque external identifiers of membership types. Results match any of the given ids.
          schema:
            maxItems: 100
            type: array
            items:
              type: string
        - name: dueFrom
          required: false
          in: query
          description: Earliest due date, inclusive.
          schema:
            format: date
            example: 2026-10-01
            type: string
        - name: dueTo
          required: false
          in: query
          description: Latest due date, inclusive.
          schema:
            format: date
            example: 2026-10-31
            type: string
        - name: paid
          required: false
          in: query
          description: True for paid invoices only, false for every other status.
          schema:
            type: boolean
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/PageDto"
                  - properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/InvoiceListItemDto"
                      meta:
                        $ref: "#/components/schemas/PageMetaDto"
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: List invoices
      tags:
        - Invoices
  /api/v1/invoices/export:
    get:
      description: Streams an .xlsx workbook of the organisation's invoices, honouring the same filters as the list.
      operationId: InvoicesController_export_v1
      parameters:
        - name: order
          required: false
          in: query
          description: Sort order on due date applied to the list.
          schema:
            default: DESC
            type: string
            enum:
              - ASC
              - DESC
        - name: page
          required: false
          in: query
          description: 1-indexed page number.
          schema:
            minimum: 1
            maximum: 90071992547409
            default: 1
            type: number
        - name: take
          required: false
          in: query
          description: Number of items per page.
          schema:
            minimum: 1
            maximum: 100
            default: 25
            type: number
        - name: statuses
          required: false
          in: query
          description: Results match any of the given statuses.
          schema:
            type: array
            items:
              type: string
              enum:
                - PENDING
                - PROCESSING
                - SUCCEEDED
                - FAILED_RETRYABLE
                - REQUIRES_CUSTOMER_ACTION
                - PENDING_AT_PSP
                - FAILED_FINAL
                - REVERSED
        - name: membershipTypeIds
          required: false
          in: query
          description: Opaque external identifiers of membership types. Results match any of the given ids.
          schema:
            maxItems: 100
            type: array
            items:
              type: string
        - name: dueFrom
          required: false
          in: query
          description: Earliest due date, inclusive.
          schema:
            format: date
            example: 2026-10-01
            type: string
        - name: dueTo
          required: false
          in: query
          description: Latest due date, inclusive.
          schema:
            format: date
            example: 2026-10-31
            type: string
        - name: paid
          required: false
          in: query
          description: True for paid invoices only, false for every other status.
          schema:
            type: boolean
      responses:
        "200":
          description: ""
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
      security:
        - los-technical-link-id: []
        - cm-jwt: []
        - cm-sso-accountguid: []
      summary: Export invoices
      tags:
        - Invoices
  /api/v1/shop/{organisationId}:
    get:
      description: Public organisation info needed to render the shop.
      operationId: ShopCatalogController_getOrganisation_v1
      parameters:
        - name: organisationId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ShopOrganisationResponseDto"
      summary: Get shop storefront info
      tags:
        - Shop
  /api/v1/shop/{organisationId}/membership-types:
    get:
      description: Membership types (with their sellable price plans) a buyer can check out with, in the requested locale.
      operationId: ShopCatalogController_listMembershipTypes_v1
      parameters:
        - name: organisationId
          required: true
          in: path
          schema:
            type: string
        - name: locale
          required: true
          in: query
          description: Locale the texts are returned in.
          schema:
            type: string
            enum:
              - nl-NL
              - en-GB
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ShopMembershipTypeResponseDto"
      summary: List sellable membership types
      tags:
        - Shop
  /api/v1/shop/{organisationId}/consents:
    get:
      description: The consent definitions the buyer is asked to accept, in the requested locale.
      operationId: ShopCatalogController_listConsents_v1
      parameters:
        - name: organisationId
          required: true
          in: path
          schema:
            type: string
        - name: locale
          required: true
          in: query
          description: Locale the texts are returned in.
          schema:
            type: string
            enum:
              - nl-NL
              - en-GB
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ShopConsentResponseDto"
      summary: List consents
      tags:
        - Shop
  /api/v1/shop/{organisationId}/checkouts:
    post:
      description: Creates a checkout and starts a payment for it; the response carries where to send the buyer to pay.
      operationId: ShopCheckoutController_create_v1
      parameters:
        - name: organisationId
          required: true
          in: path
          schema:
            type: string
        - name: Idempotency-Key
          in: header
          description: Caller-generated key; a retried request with the same key returns the original checkout.
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateShopCheckoutSchema"
      responses:
        "201":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ShopCheckoutResponseDto"
      summary: Start a checkout
      tags:
        - Shop
  /api/v1/shop/{organisationId}/checkouts/{checkoutId}:
    get:
      description: Polls the status of a checkout (e.g. while the buyer completes payment).
      operationId: ShopCheckoutController_getOne_v1
      parameters:
        - name: organisationId
          required: true
          in: path
          schema:
            type: string
        - name: checkoutId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ShopCheckoutResponseDto"
      summary: Get a checkout
      tags:
        - Shop
  /api/v1/shop/{organisationId}/checkouts/{checkoutId}/cancel:
    post:
      description: Gives up an unpaid checkout at the payment provider. A checkout that was paid in the meantime stays paid.
      operationId: ShopCheckoutController_cancel_v1
      parameters:
        - name: organisationId
          required: true
          in: path
          schema:
            type: string
        - name: checkoutId
          required: true
          in: path
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ShopCheckoutResponseDto"
      summary: Cancel a checkout
      tags:
        - Shop
info:
  title: Memberships API
  description: CM Live Membership REST API. Tenant is the `x-los-technical-link-id` request header; caller identity is a JWT bearer token or, as a fallback, the `x-cm-sso-accountguid` header — set either via the Authorize button.
  version: "1.0"
  contact: {}
tags: []
servers: []
components:
  securitySchemes:
    cm-jwt:
      scheme: bearer
      bearerFormat: JWT
      type: http
    cm-sso-accountguid:
      type: apiKey
      in: header
      name: x-cm-sso-accountguid
    los-technical-link-id:
      type: apiKey
      in: header
      name: x-los-technical-link-id
      description: Opaque tenant identifier (the CM technical-link UUID) that scopes the request to a single organisation. Resolved and access-checked by the platform guard before the handler runs.
  schemas:
    CurrentUserResponseDto:
      type: object
      properties:
        displayName:
          type: string
          description: CM display name (first + last name), or null if unavailable.
          nullable: true
        firstName:
          type: string
          description: CM first name, or null if unavailable.
          nullable: true
        email:
          type: string
          description: CM default email address, or null if unavailable.
          nullable: true
      required:
        - displayName
        - firstName
        - email
    OrganisationResponseDto:
      type: object
      properties:
        organisationId:
          type: string
          description: Opaque identifier for the organisation (the tenant key carried in the x-los-technical-link-id request header).
          format: uuid
        name:
          type: string
          description: Human-readable organisation name.
        origin:
          type: string
          enum:
            - CM_PLATFORM
            - MANUAL
          description: How the organisation was created.
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO-4217 alpha-3 currency code.
          example: EUR
        languageCode:
          type: string
          enum:
            - DE
            - EN
            - ES
            - FR
            - HE
            - NL
          description: CM upper-case language code.
          example: EN
        shopUrl:
          type: string
          nullable: true
          format: uri
          description: Public link to the organisation's shop; null when the deployment has no shop URL configured.
        createdAt:
          format: date-time
          type: string
          description: When the organisation was first provisioned.
        updatedAt:
          format: date-time
          type: string
          description: When the organisation was last updated.
      required:
        - organisationId
        - name
        - origin
        - currencyCode
        - languageCode
        - shopUrl
        - createdAt
        - updatedAt
    MembersPerTypeItemDto:
      type: object
      properties:
        membershipTypeName:
          type: object
          additionalProperties:
            type: string
          description: Name of the membership type, keyed by language.
        count:
          type: number
          description: Number of active members with this membership type.
      required:
        - membershipTypeName
        - count
    OverviewStatsResponseDto:
      type: object
      properties:
        activeMembers:
          type: number
          description: Total number of members with an active membership.
        newMembersThisMonth:
          type: number
          description: Number of members whose link was created since the start of the current month.
        membersPerType:
          description: Active members broken down by membership type.
          type: array
          items:
            $ref: "#/components/schemas/MembersPerTypeItemDto"
      required:
        - activeMembers
        - newMembersThisMonth
        - membersPerType
    PartySchema:
      type: object
      properties:
        type:
          type: string
          enum:
            - NATURAL_PERSON
            - ORGANISATION
          example: NATURAL_PERSON
        initials:
          type: string
          description: Natural person only.
          example: P.J.
          nullable: true
        givenName:
          type: string
          description: Natural person only. Required when `type` is `NATURAL_PERSON`.
          example: Anna
        familyName:
          type: string
          description: Natural person only. Required when `type` is `NATURAL_PERSON`.
          example: de Vries
        legalName:
          type: string
          description: Organisation only. Required when `type` is `ORGANISATION`.
          example: CM.com B.V.
        gender:
          type: string
          enum:
            - MALE
            - FEMALE
            - OTHER
          description: Natural person only.
          example: FEMALE
        dateOfBirth:
          type: string
          format: date
          description: Natural person only.
          example: 1990-05-14
          nullable: true
        email:
          type: string
          example: anna.devries@example.com
          nullable: true
        phonePrimary:
          type: string
          example: "+31612345678"
          nullable: true
        phoneSecondary:
          type: string
          example: "+31612345679"
          nullable: true
      required:
        - type
    PartyResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the party.
        type:
          type: string
          enum:
            - NATURAL_PERSON
            - ORGANISATION
          description: Whether the party is a natural person or an organisation.
        initials:
          type: string
          description: Initials. Natural person only.
          nullable: true
        givenName:
          type: string
          description: Given name. Natural person only.
          nullable: true
        familyName:
          type: string
          description: Family name. Natural person only.
          nullable: true
        legalName:
          type: string
          description: Legal name. Organisation only.
          nullable: true
        gender:
          type: string
          enum:
            - MALE
            - FEMALE
            - OTHER
            - null
          description: Gender. Natural person only.
          nullable: true
        dateOfBirth:
          type: string
          format: date
          description: Date of birth. Natural person only.
          nullable: true
        email:
          type: string
          description: Email address.
          nullable: true
        phonePrimary:
          type: string
          description: Primary phone number.
          nullable: true
        phoneSecondary:
          type: string
          description: Secondary phone number.
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: When the party was created.
        updatedAt:
          format: date-time
          type: string
          description: When the party was last updated.
        roles:
          type: array
          description: The distinct link roles this party holds across its memberships.
          items:
            type: string
            enum:
              - OWNER
              - MEMBER
      required:
        - id
        - type
        - initials
        - givenName
        - familyName
        - legalName
        - gender
        - dateOfBirth
        - email
        - phonePrimary
        - phoneSecondary
        - createdAt
        - updatedAt
        - roles
    PageMetaDto:
      type: object
      properties:
        page:
          type: number
        take:
          type: number
        itemCount:
          type: number
        pageCount:
          type: number
        hasPreviousPage:
          type: boolean
        hasNextPage:
          type: boolean
      required:
        - page
        - take
        - itemCount
        - pageCount
        - hasPreviousPage
        - hasNextPage
    PageDto:
      type: object
      properties:
        data:
          type: array
          items:
            type: array
        meta:
          $ref: "#/components/schemas/PageMetaDto"
      required:
        - data
        - meta
    UpdatePartyDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - NATURAL_PERSON
            - ORGANISATION
          example: NATURAL_PERSON
        initials:
          type: string
          description: Natural person only.
          example: P.J.
          nullable: true
        givenName:
          type: string
          description: Natural person only. Required when `type` is `NATURAL_PERSON`.
          example: Anna
        familyName:
          type: string
          description: Natural person only. Required when `type` is `NATURAL_PERSON`.
          example: de Vries
        legalName:
          type: string
          description: Organisation only. Required when `type` is `ORGANISATION`.
          example: CM.com B.V.
        gender:
          type: string
          enum:
            - MALE
            - FEMALE
            - OTHER
          description: Natural person only.
          example: FEMALE
        dateOfBirth:
          type: string
          format: date
          description: Natural person only.
          example: 1990-05-14
          nullable: true
        email:
          type: string
          example: anna.devries@example.com
          nullable: true
        phonePrimary:
          type: string
          example: "+31612345678"
          nullable: true
        phoneSecondary:
          type: string
          example: "+31612345679"
          nullable: true
    CreateAddressSchema:
      type: object
      properties:
        kind:
          type: string
          enum:
            - POSTAL
            - BILLING
          default: POSTAL
          description: Defaults to POSTAL.
        attentionLine:
          type: string
          description: Company/department to address the mail to.
          example: Finance Department
        street:
          type: string
          example: Stationsplein
        houseNumber:
          type: string
          example: "9"
        houseNumberSuffix:
          type: string
          example: K
        postalCode:
          type: string
          example: 4811 BM
        city:
          type: string
          example: Breda
        countryCode:
          type: string
          description: ISO 3166-1 alpha-2 country code. Defaults to NL.
          example: NL
        validFrom:
          type: string
          format: date
          description: Defaults to today.
          example: 2026-08-17
      required:
        - street
        - houseNumber
        - postalCode
        - city
    AddressResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the address.
        kind:
          type: string
          enum:
            - POSTAL
            - BILLING
          description: What this address is used for.
        attentionLine:
          type: string
          description: Company/department to address the mail to.
          nullable: true
        street:
          type: string
          description: Street name.
        houseNumber:
          type: string
          description: House number.
        houseNumberSuffix:
          type: string
          description: House number suffix.
          nullable: true
        postalCode:
          type: string
          description: Postal code.
        city:
          type: string
          description: City.
        countryCode:
          type: string
          description: ISO 3166-1 alpha-2 country code.
        validFrom:
          type: string
          format: date
          description: Date from which this address is valid.
          example: 2026-08-17
        validTo:
          type: string
          format: date
          description: Date until which this address was valid, if closed.
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: When the address was created.
        updatedAt:
          format: date-time
          type: string
          description: When the address was last updated.
      required:
        - id
        - kind
        - street
        - houseNumber
        - postalCode
        - city
        - countryCode
        - validFrom
        - validTo
        - createdAt
        - updatedAt
    CorrectAddressSchema:
      type: object
      properties:
        attentionLine:
          type: string
          description: Company/department to address the mail to.
          example: Finance Department
        street:
          type: string
          example: Stationsplein
        houseNumber:
          type: string
          example: "9"
        houseNumberSuffix:
          type: string
          example: K
        postalCode:
          type: string
          example: 4811 BM
        city:
          type: string
          example: Breda
        countryCode:
          type: string
          description: ISO 3166-1 alpha-2 country code. Defaults to NL.
          example: NL
      required:
        - street
        - houseNumber
        - postalCode
        - city
    MoveAddressSchema:
      type: object
      properties:
        kind:
          type: string
          enum:
            - POSTAL
            - BILLING
          default: POSTAL
          description: Defaults to POSTAL.
        attentionLine:
          type: string
          description: Company/department to address the mail to.
          example: Finance Department
        street:
          type: string
          example: Stationsplein
        houseNumber:
          type: string
          example: "9"
        houseNumberSuffix:
          type: string
          example: K
        postalCode:
          type: string
          example: 4811 BM
        city:
          type: string
          example: Breda
        countryCode:
          type: string
          description: ISO 3166-1 alpha-2 country code. Defaults to NL.
          example: NL
        effectiveDate:
          type: string
          format: date
          description: The new address becomes current on this date.
          example: 2026-09-01
      required:
        - street
        - houseNumber
        - postalCode
        - city
        - effectiveDate
    NoteSchema:
      type: object
      properties:
        text:
          type: string
          example: Called the customer, they confirmed the new address.
      required:
        - text
    NoteResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the note.
        partyId:
          type: string
          description: Opaque external identifier for the note's party (the route's :id).
        text:
          type: string
          description: Free-text note content.
        createdByAccountGuid:
          type: string
          description: Account GUID of the user who created the note.
        author:
          type: string
          description: Display name of the note's author, or `null` if it couldn't be resolved.
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: When the note was created.
      required:
        - id
        - partyId
        - text
        - createdByAccountGuid
        - author
        - createdAt
    TranslatedTextSchema:
      type: object
      properties:
        DE:
          type: string
        EN:
          type: string
        ES:
          type: string
        FR:
          type: string
        HE:
          type: string
        NL:
          type: string
    CreateMembershipTypeSchema:
      type: object
      properties:
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
          description: Status of the membership type. Omit to leave unchanged on update; defaults to ACTIVE on create.
        productType:
          type: string
          enum:
            - MEMBERSHIP
            - DONATION
          description: Product type of the membership type. Omit to leave unchanged on update; defaults to MEMBERSHIP on create.
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
          description: Public (shops) or back-office only. Omit to leave unchanged on update; defaults to PUBLIC on create.
        description:
          description: Optional human-readable description per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        maxMembers:
          type: number
          nullable: true
          minimum: 1
          description: Maximum number of members; null means unlimited.
        trialPeriodDays:
          type: number
          minimum: 0
          maximum: 3650
          description: Whole days after a membership starts that count as its trial. Omit to leave unchanged; defaults to 0 on create.
        gracePeriodDays:
          type: number
          minimum: 0
          maximum: 3650
          description: Whole days after a membership ends that count as its grace period. Omit to leave unchanged; defaults to 0 on create.
        name:
          description: Human-readable membership type name per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
      required:
        - name
    MembershipTypeResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the membership type.
        name:
          type: object
          additionalProperties:
            type: string
          description: Human-readable membership type name, keyed by language.
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
          description: Whether the membership type is active.
        productType:
          type: string
          enum:
            - MEMBERSHIP
            - DONATION
          description: Product type of the membership type.
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
          description: Public (shops) or back-office only.
        description:
          type: object
          additionalProperties:
            type: string
          description: Optional human-readable description, keyed by language; empty when unset.
        maxMembers:
          type: number
          nullable: true
          description: Maximum number of members; null means unlimited.
        trialPeriodDays:
          type: number
          description: Whole days after a membership starts that count as its trial.
        gracePeriodDays:
          type: number
          description: Whole days after a membership ends that count as its grace period.
        pricePlanCount:
          type: number
          description: Number of price plans defined for this membership type.
        createdAt:
          format: date-time
          type: string
          description: When the membership type was created.
        updatedAt:
          format: date-time
          type: string
          description: When the membership type was last updated.
      required:
        - id
        - name
        - status
        - productType
        - visibility
        - description
        - maxMembers
        - trialPeriodDays
        - gracePeriodDays
        - pricePlanCount
        - createdAt
        - updatedAt
    UpdateMembershipTypeSchema:
      type: object
      properties:
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
          description: Status of the membership type. Omit to leave unchanged on update; defaults to ACTIVE on create.
        productType:
          type: string
          enum:
            - MEMBERSHIP
            - DONATION
          description: Product type of the membership type. Omit to leave unchanged on update; defaults to MEMBERSHIP on create.
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
          description: Public (shops) or back-office only. Omit to leave unchanged on update; defaults to PUBLIC on create.
        description:
          description: Optional human-readable description per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        maxMembers:
          type: number
          nullable: true
          minimum: 1
          description: Maximum number of members; null means unlimited.
        trialPeriodDays:
          type: number
          minimum: 0
          maximum: 3650
          description: Whole days after a membership starts that count as its trial. Omit to leave unchanged; defaults to 0 on create.
        gracePeriodDays:
          type: number
          minimum: 0
          maximum: 3650
          description: Whole days after a membership ends that count as its grace period. Omit to leave unchanged; defaults to 0 on create.
        name:
          description: Human-readable membership type name per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
    VatRateResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the VAT rate.
        countryCode:
          type: string
          description: ISO 3166-1 alpha-2 country code, uppercase.
          example: NL
        rateType:
          type: string
          enum:
            - STANDARD
            - REDUCED
            - ZERO
          description: Which statutory rate this is.
        percentage:
          type: number
          description: "The rate as a percentage, not a fraction: 21 means 21%."
          example: 21
        createdAt:
          format: date-time
          type: string
          description: When the rate was recorded.
        updatedAt:
          format: date-time
          type: string
          description: When the rate was last updated.
      required:
        - id
        - countryCode
        - rateType
        - percentage
        - createdAt
        - updatedAt
    SettingsItemResponseDto:
      type: object
      properties:
        key:
          type: string
          enum:
            - application.languages
            - application.language
            - application.currency
            - application.timezone
            - application.date-format
            - application.time-format
            - organisation.contact-email
            - organisation.contact-phone
            - organisation.website-url
            - organisation.terms-url
            - organisation.privacy-url
            - branding.primary-color
            - branding.secondary-color
            - communication.mail-from-name
            - communication.mail-from-address
            - communication.send-confirmation-mail
            - communication.send-reminder-mail
            - identification.relation-start-sequence
            - identification.membership-prefix
            - cm.product-token
            - cm.pay.ps.username
            - cm.pay.ps.password
            - cm.pay.ps.merchant-id
            - shop.enabled
            - shop.donations-enabled
            - shop.gifts-enabled
            - shop.slug
            - shop.styling.theme
            - shop.styling.box-style
          description: The settings key.
        value:
          nullable: true
          description: The effective value; always null for a secret, which only reports whether it is set.
          oneOf:
            - type: string
            - type: array
              items:
                type: string
            - type: number
            - type: boolean
        isSet:
          type: boolean
          description: Whether an explicit value is stored for this key.
      required:
        - key
        - value
        - isSet
    SettingsResponseDto:
      type: object
      properties:
        items:
          description: The effective settings for the tenant.
          type: array
          items:
            $ref: "#/components/schemas/SettingsItemResponseDto"
      required:
        - items
    UpsertSettingItemSchema:
      type: object
      properties:
        key:
          type: string
          enum:
            - application.languages
            - application.language
            - application.currency
            - application.timezone
            - application.date-format
            - application.time-format
            - organisation.contact-email
            - organisation.contact-phone
            - organisation.website-url
            - organisation.terms-url
            - organisation.privacy-url
            - branding.primary-color
            - branding.secondary-color
            - communication.mail-from-name
            - communication.mail-from-address
            - communication.send-confirmation-mail
            - communication.send-reminder-mail
            - identification.relation-start-sequence
            - identification.membership-prefix
            - cm.product-token
            - cm.pay.ps.username
            - cm.pay.ps.password
            - cm.pay.ps.merchant-id
            - shop.enabled
            - shop.donations-enabled
            - shop.gifts-enabled
            - shop.slug
            - shop.styling.theme
            - shop.styling.box-style
          description: The settings key to set.
        value:
          description: The value for this key; its type is defined by the catalog (string, string list, number or boolean).
          oneOf:
            - type: string
            - type: array
              items:
                type: string
            - type: number
            - type: boolean
      required:
        - key
        - value
    UpsertSettingSchema:
      type: object
      properties:
        items:
          description: The settings items to upsert.
          type: array
          items:
            $ref: "#/components/schemas/UpsertSettingItemSchema"
      required:
        - items
    CreateCustomFieldDefinitionSchema:
      type: object
      properties:
        kind:
          type: string
          enum:
            - CUSTOM_FIELD
            - CONSENT
        scope:
          type: string
          enum:
            - PARTY
            - MEMBERSHIP
        key:
          type: string
          example: custom_field.shirt_size
        label:
          $ref: "#/components/schemas/TranslatedTextSchema"
        type:
          type: string
          enum:
            - TEXT
        required:
          type: boolean
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
        defaultValue:
          type: string
          nullable: true
        sortOrder:
          type: number
      required:
        - kind
        - scope
        - key
        - label
        - type
        - required
        - visibility
        - sortOrder
    CustomFieldDefinitionResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the definition.
        kind:
          type: string
          enum:
            - CUSTOM_FIELD
            - CONSENT
          description: Discriminator; custom field or (later) consent.
        scope:
          type: string
          enum:
            - PARTY
            - MEMBERSHIP
          description: Entity the field attaches to.
        key:
          type: string
          description: Stable machine key, unique per scope within the tenant.
        label:
          type: object
          additionalProperties:
            type: string
          description: Translated labels keyed by language.
        type:
          type: string
          enum:
            - TEXT
          description: Value type; only text in v1.
        required:
          type: boolean
          description: Whether a value is mandatory.
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
          description: Public (shops) or back-office only.
        defaultValue:
          type: string
          nullable: true
          description: Default/prefilled value, or null.
        sortOrder:
          type: number
          description: Display order within a scope.
        createdAt:
          format: date-time
          type: string
          description: When the definition was created.
        updatedAt:
          format: date-time
          type: string
          description: When the definition was last updated.
      required:
        - id
        - kind
        - scope
        - key
        - label
        - type
        - required
        - visibility
        - defaultValue
        - sortOrder
        - createdAt
        - updatedAt
    UpdateCustomFieldDefinitionSchema:
      type: object
      properties:
        label:
          $ref: "#/components/schemas/TranslatedTextSchema"
        type:
          type: string
          enum:
            - TEXT
        required:
          type: boolean
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
        defaultValue:
          type: string
          nullable: true
        sortOrder:
          type: number
      required:
        - label
        - type
        - required
        - visibility
        - sortOrder
    CustomFieldEntryResponseDto:
      type: object
      properties:
        definition:
          description: The field definition.
          allOf:
            - $ref: "#/components/schemas/CustomFieldDefinitionResponseDto"
        value:
          type: string
          nullable: true
          description: Stored value for the target entity, or null.
      required:
        - definition
        - value
    CustomFieldValuesResponseDto:
      type: object
      properties:
        items:
          description: Definitions in scope with their current values.
          type: array
          items:
            $ref: "#/components/schemas/CustomFieldEntryResponseDto"
      required:
        - items
    UpsertCustomFieldValueItemSchema:
      type: object
      properties:
        definitionId:
          type: string
          description: Opaque ulid of the custom-field definition this value belongs to.
        value:
          type: string
          nullable: true
      required:
        - definitionId
    UpsertCustomFieldValuesSchema:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/UpsertCustomFieldValueItemSchema"
      required:
        - items
    MessageTemplateResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the message template.
        type:
          type: string
          enum:
            - WELCOME
        subject:
          type: object
          additionalProperties:
            type: string
          description: Email subject, keyed by language.
        preheader:
          type: object
          additionalProperties:
            type: string
          description: Email preheader, keyed by language.
        body:
          type: object
          additionalProperties:
            type: string
          description: Email body (HTML), keyed by language.
        buttonLabel:
          type: object
          additionalProperties:
            type: string
          description: Call-to-action button label, keyed by language.
        isDefault:
          type: boolean
          description: True when this org has not yet customised this template — still following the global default content.
      required:
        - id
        - type
        - subject
        - preheader
        - body
        - buttonLabel
        - isDefault
    MessageTemplateListResponseDto:
      type: object
      properties:
        senderName:
          type: string
          description: The tenant's sender name, shown as the email sender. Taken from the sender name setting, falling back to the organisation name when that is blank.
        templates:
          type: array
          items:
            $ref: "#/components/schemas/MessageTemplateResponseDto"
      required:
        - senderName
        - templates
    UpdateMessageTemplateSchema:
      type: object
      properties:
        subject:
          description: Email subject per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        preheader:
          description: Email preheader per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        body:
          description: Email body per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        buttonLabel:
          description: Call-to-action button label per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
    PreviewMessageTemplateSchema:
      type: object
      properties:
        preheader:
          type: string
          maxLength: 200
        body:
          type: string
          maxLength: 20000
        buttonLabel:
          type: string
          maxLength: 60
      required:
        - preheader
        - body
        - buttonLabel
    MessageTemplatePreviewResponseDto:
      type: object
      properties:
        html:
          type: string
          description: Rendered HTML returned by mail-broker.
      required:
        - html
    CreateConsentDefinitionSchema:
      type: object
      properties:
        key:
          type: string
          example: consent.privacy_policy
        label:
          $ref: "#/components/schemas/TranslatedTextSchema"
        mandatory:
          type: boolean
          description: When true, the consent must be granted before a sale can complete.
        legalText:
          nullable: true
          type: object
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        documentUrl:
          type: string
          nullable: true
        sortOrder:
          type: number
      required:
        - key
        - label
        - mandatory
        - sortOrder
    ConsentDefinitionResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the definition.
        key:
          type: string
          description: Stable machine key, unique within the tenant.
        label:
          type: object
          additionalProperties:
            type: string
          description: Translated labels keyed by language.
        mandatory:
          type: boolean
          description: Whether the consent must be granted before a sale can complete.
        version:
          type: number
          description: Bumped whenever the legal text changes; each record snapshots the version agreed to.
        legalText:
          type: object
          additionalProperties:
            type: string
          nullable: true
          description: Translated legal wording, or null.
        documentUrl:
          type: string
          nullable: true
          description: Link to a backing document or web page. Stored but unused in v1.
        sortOrder:
          type: number
          description: Display order.
        createdAt:
          format: date-time
          type: string
          description: When the definition was created.
        updatedAt:
          format: date-time
          type: string
          description: When the definition was last updated.
      required:
        - id
        - key
        - label
        - mandatory
        - version
        - legalText
        - documentUrl
        - sortOrder
        - createdAt
        - updatedAt
    ConsentDefinitionsResponseDto:
      type: object
      properties:
        items:
          description: The tenant consent definitions.
          type: array
          items:
            $ref: "#/components/schemas/ConsentDefinitionResponseDto"
      required:
        - items
    UpdateConsentDefinitionSchema:
      type: object
      properties:
        label:
          $ref: "#/components/schemas/TranslatedTextSchema"
        mandatory:
          type: boolean
        legalText:
          nullable: true
          type: object
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        documentUrl:
          type: string
          nullable: true
        sortOrder:
          type: number
      required:
        - label
        - mandatory
        - sortOrder
    AccessBenefitConfigSchema:
      type: object
      properties:
        type:
          type: string
          enum:
            - ACCESS
      required:
        - type
    ProductDiscountBenefitConfigSchema:
      type: object
      properties:
        type:
          type: string
          enum:
            - PRODUCT_DISCOUNT
        discountKind:
          type: string
          enum:
            - PERCENTAGE
            - FIXED_AMOUNT
        value:
          type: number
          description: Percentage (0-100) or amount in the organisation currency.
          example: 10
        includeProductRefs:
          maxItems: 100
          description: External product references the discount applies to; empty means every product.
          type: array
          items:
            type: string
        excludeProductRefs:
          maxItems: 100
          description: External product references excluded from the discount.
          type: array
          items:
            type: string
      required:
        - type
        - discountKind
        - value
        - includeProductRefs
        - excludeProductRefs
    ValidityConditionSchema:
      type: object
      properties:
        field:
          type: string
          enum:
            - WEEKDAY
            - DATE
            - TIME
            - MEMBERSHIP_STATUS
        operator:
          type: string
          enum:
            - IN
            - IS
            - BETWEEN
          description: Must suit the field, e.g. IN for WEEKDAY, BETWEEN for TIME.
        value:
          example:
            - 18:00
            - 22:00
          description: Weekday names, `YYYY-MM-DD` dates or `HH:mm` times (from, until), depending on the field.
          type: array
          items:
            type: string
      required:
        - field
        - operator
        - value
    ValidityRuleSchema:
      type: object
      properties:
        name:
          type: string
          example: Freaky Friday
        priority:
          type: number
          minimum: 1
          maximum: 1000
          description: Unique per benefit; the lowest priority that matches decides.
        action:
          type: string
          enum:
            - ALLOW
            - DENY
        conditions:
          description: All conditions must hold for the rule to match.
          type: array
          items:
            $ref: "#/components/schemas/ValidityConditionSchema"
      required:
        - name
        - priority
        - action
        - conditions
    CreateBenefitSchema:
      type: object
      properties:
        description:
          description: Optional human-readable description per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
          description: Defaults to ACTIVE on create.
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
          description: Defaults to PRIVATE on create.
        config:
          description: Its `type` must match the benefit type. Required for PRODUCT_DISCOUNT; on update, omit it to keep the stored config.
          oneOf:
            - $ref: "#/components/schemas/AccessBenefitConfigSchema"
            - $ref: "#/components/schemas/ProductDiscountBenefitConfigSchema"
          discriminator:
            propertyName: type
            mapping:
              ACCESS: "#/components/schemas/AccessBenefitConfigSchema"
              PRODUCT_DISCOUNT: "#/components/schemas/ProductDiscountBenefitConfigSchema"
        validityRules:
          maxItems: 20
          description: Decide when the benefit can be used; with no matching rule the membership period decides. Replaces the stored list.
          type: array
          items:
            $ref: "#/components/schemas/ValidityRuleSchema"
        usageLimitCount:
          type: number
          nullable: true
          minimum: 1
          maximum: 10000
          description: Maximum uses per window; null means unlimited.
        usageLimitWindow:
          type: string
          enum:
            - TOTAL
            - DAY
            - MEMBERSHIP_YEAR
          description: Cleared together with the scope when `usageLimitCount` is null.
        usageLimitScope:
          type: string
          enum:
            - MEMBER
            - MEMBERSHIP
        visibleUntil:
          type: string
          format: date
          nullable: true
          description: Last day the benefit is returned by the API.
        key:
          type: string
          example: benefit.summer_evening
          description: Unique within the organisation and immutable after creation. Generated when omitted.
        name:
          description: Human-readable benefit name per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: Immutable after creation.
        visibleFrom:
          type: string
          format: date
          description: First day the benefit is returned by the API.
      required:
        - name
        - type
        - visibleFrom
    AccessBenefitConfigDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - ACCESS
      required:
        - type
    ProductDiscountBenefitConfigDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - PRODUCT_DISCOUNT
        discountKind:
          type: string
          enum:
            - PERCENTAGE
            - FIXED_AMOUNT
        value:
          type: number
          description: Percentage (0-100) or amount in `currencyCode`.
        currencyCode:
          type: string
          nullable: true
          description: Set for FIXED_AMOUNT only.
        includeProductRefs:
          description: External product references; empty means every product.
          type: array
          items:
            type: string
        excludeProductRefs:
          type: array
          items:
            type: string
      required:
        - type
        - discountKind
        - value
        - currencyCode
        - includeProductRefs
        - excludeProductRefs
    ValidityConditionDto:
      type: object
      properties:
        field:
          type: string
          enum:
            - WEEKDAY
            - DATE
            - TIME
            - MEMBERSHIP_STATUS
        operator:
          type: string
          enum:
            - IN
            - IS
            - BETWEEN
        value:
          example:
            - 18:00
            - 22:00
          description: Weekday names, dates, times or whole days, depending on the field.
          type: array
          items:
            type: string
      required:
        - field
        - operator
        - value
    ValidityRuleDto:
      type: object
      properties:
        name:
          type: string
          example: Freaky Friday
        priority:
          type: number
          description: Unique per benefit; the lowest priority that matches decides.
        action:
          type: string
          enum:
            - ALLOW
            - DENY
        conditions:
          description: All conditions must hold for the rule to match.
          type: array
          items:
            $ref: "#/components/schemas/ValidityConditionDto"
      required:
        - name
        - priority
        - action
        - conditions
    BenefitResponseDto:
      type: object
      properties:
        id:
          type: string
          description: External identifier for the benefit.
        key:
          type: string
          example: benefit.summer_evening
          description: Stable machine key, unique within the tenant.
        name:
          type: object
          additionalProperties:
            type: string
        description:
          type: object
          additionalProperties:
            type: string
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
        config:
          description: The config of the benefit type; narrow on `type`.
          oneOf:
            - $ref: "#/components/schemas/AccessBenefitConfigDto"
            - $ref: "#/components/schemas/ProductDiscountBenefitConfigDto"
          discriminator:
            propertyName: type
            mapping:
              ACCESS: "#/components/schemas/AccessBenefitConfigDto"
              PRODUCT_DISCOUNT: "#/components/schemas/ProductDiscountBenefitConfigDto"
        validityRules:
          description: In priority order; with no matching rule the benefit does not apply.
          type: array
          items:
            $ref: "#/components/schemas/ValidityRuleDto"
        usageLimitCount:
          type: number
          nullable: true
          description: Null means unlimited.
        usageLimitWindow:
          type: string
          enum:
            - TOTAL
            - DAY
            - MEMBERSHIP_YEAR
          nullable: true
        usageLimitScope:
          type: string
          enum:
            - MEMBER
            - MEMBERSHIP
          nullable: true
        visibleFrom:
          type: string
          format: date
        visibleUntil:
          type: string
          format: date
          nullable: true
        createdAt:
          format: date-time
          type: string
        updatedAt:
          format: date-time
          type: string
      required:
        - id
        - key
        - name
        - description
        - type
        - status
        - visibility
        - config
        - validityRules
        - usageLimitCount
        - usageLimitWindow
        - usageLimitScope
        - visibleFrom
        - visibleUntil
        - createdAt
        - updatedAt
    LinkedMembershipTypeDto:
      type: object
      properties:
        id:
          type: string
          description: External identifier for the membership type.
        name:
          type: object
          additionalProperties:
            type: string
      required:
        - id
        - name
    BenefitDetailResponseDto:
      type: object
      properties:
        id:
          type: string
          description: External identifier for the benefit.
        key:
          type: string
          example: benefit.summer_evening
          description: Stable machine key, unique within the tenant.
        name:
          type: object
          additionalProperties:
            type: string
        description:
          type: object
          additionalProperties:
            type: string
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
        config:
          description: The config of the benefit type; narrow on `type`.
          oneOf:
            - $ref: "#/components/schemas/AccessBenefitConfigDto"
            - $ref: "#/components/schemas/ProductDiscountBenefitConfigDto"
          discriminator:
            propertyName: type
            mapping:
              ACCESS: "#/components/schemas/AccessBenefitConfigDto"
              PRODUCT_DISCOUNT: "#/components/schemas/ProductDiscountBenefitConfigDto"
        validityRules:
          description: In priority order; with no matching rule the benefit does not apply.
          type: array
          items:
            $ref: "#/components/schemas/ValidityRuleDto"
        usageLimitCount:
          type: number
          nullable: true
          description: Null means unlimited.
        usageLimitWindow:
          type: string
          enum:
            - TOTAL
            - DAY
            - MEMBERSHIP_YEAR
          nullable: true
        usageLimitScope:
          type: string
          enum:
            - MEMBER
            - MEMBERSHIP
          nullable: true
        visibleFrom:
          type: string
          format: date
        visibleUntil:
          type: string
          format: date
          nullable: true
        createdAt:
          format: date-time
          type: string
        updatedAt:
          format: date-time
          type: string
        linkedMembershipTypes:
          description: Membership types this benefit is linked to.
          type: array
          items:
            $ref: "#/components/schemas/LinkedMembershipTypeDto"
      required:
        - id
        - key
        - name
        - description
        - type
        - status
        - visibility
        - config
        - validityRules
        - usageLimitCount
        - usageLimitWindow
        - usageLimitScope
        - visibleFrom
        - visibleUntil
        - createdAt
        - updatedAt
        - linkedMembershipTypes
    UpdateBenefitSchema:
      type: object
      properties:
        description:
          description: Optional human-readable description per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        status:
          type: string
          enum:
            - ACTIVE
            - INACTIVE
          description: Defaults to ACTIVE on create.
        visibility:
          type: string
          enum:
            - PUBLIC
            - PRIVATE
          description: Defaults to PRIVATE on create.
        config:
          description: Its `type` must match the benefit type. Required for PRODUCT_DISCOUNT; on update, omit it to keep the stored config.
          oneOf:
            - $ref: "#/components/schemas/AccessBenefitConfigSchema"
            - $ref: "#/components/schemas/ProductDiscountBenefitConfigSchema"
          discriminator:
            propertyName: type
            mapping:
              ACCESS: "#/components/schemas/AccessBenefitConfigSchema"
              PRODUCT_DISCOUNT: "#/components/schemas/ProductDiscountBenefitConfigSchema"
        validityRules:
          maxItems: 20
          description: Decide when the benefit can be used; with no matching rule the membership period decides. Replaces the stored list.
          type: array
          items:
            $ref: "#/components/schemas/ValidityRuleSchema"
        usageLimitCount:
          type: number
          nullable: true
          minimum: 1
          maximum: 10000
          description: Maximum uses per window; null means unlimited.
        usageLimitWindow:
          type: string
          enum:
            - TOTAL
            - DAY
            - MEMBERSHIP_YEAR
          description: Cleared together with the scope when `usageLimitCount` is null.
        usageLimitScope:
          type: string
          enum:
            - MEMBER
            - MEMBERSHIP
        visibleUntil:
          type: string
          format: date
          nullable: true
          description: Last day the benefit is returned by the API.
        name:
          description: Human-readable benefit name per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        visibleFrom:
          type: string
          format: date
          description: First day the benefit is returned by the API.
    LinkBenefitSchema:
      type: object
      properties:
        benefitId:
          type: string
          description: External identifier of the benefit to link.
      required:
        - benefitId
    BenefitUsageBenefitDto:
      type: object
      properties:
        key:
          type: string
          example: benefit.lounge_access
          description: Stable key of the benefit.
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: Kind of benefit, e.g. ACCESS (entry to a venue or facility) or PRODUCT_DISCOUNT (a discount on purchases).
        name:
          type: object
          additionalProperties:
            type: string
          description: Display name of the benefit per language.
      required:
        - key
        - type
        - name
    BenefitUsageMemberDto:
      type: object
      properties:
        membershipNumber:
          type: string
          example: "2662667052"
          description: The membership number that was scanned.
        partyId:
          type: string
          nullable: true
          description: Party id; null when the scanned membership number is not linked to a party.
        name:
          type: string
          nullable: true
          description: Display name; null when the scanned membership number is not linked to a party.
      required:
        - membershipNumber
        - partyId
        - name
    BenefitUsageListItemDto:
      type: object
      properties:
        id:
          type: string
          description: External identifier for the use.
        result:
          type: string
          enum:
            - ALLOWED
            - MEMBERSHIP_NOT_FOUND
            - MEMBERSHIP_NOT_ENTITLED
            - MEMBERSHIP_BLOCKED
            - NOT_APPLICABLE
            - DENIED_BY_RULE
            - LIMIT_REACHED
          description: ALLOWED for a successful use, otherwise why the attempt was refused.
        benefit:
          nullable: true
          description: Null when a denied attempt matched no benefit.
          type: object
          allOf:
            - $ref: "#/components/schemas/BenefitUsageBenefitDto"
        member:
          $ref: "#/components/schemas/BenefitUsageMemberDto"
        quantity:
          type: number
        businessDate:
          type: string
          format: date
          description: The calendar day, in the organisation’s local time, that this use counts toward.
        createdAt:
          format: date-time
          type: string
        source:
          type: string
          enum:
            - API
          description: The channel the use was registered through; currently only API.
        locationReference:
          type: string
          nullable: true
          description: Where the use was registered, e.g. a gate, till or kiosk, as sent by the caller; null when not given.
        externalReference:
          type: string
          nullable: true
          description: The caller’s own reference for the use, e.g. a receipt or ticket number; null when not given.
        reversedAt:
          format: date-time
          type: string
          nullable: true
          description: Set once the use has been reversed.
      required:
        - id
        - result
        - benefit
        - member
        - quantity
        - businessDate
        - createdAt
        - source
        - locationReference
        - externalReference
        - reversedAt
    ValidateBenefitSchema:
      type: object
      properties:
        membershipNumber:
          type: string
          example: "2662667052"
          description: The membership number on the member's card.
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: "The question asked: may this member enter, or which discount applies."
        benefitKey:
          type: string
          example: benefit.free_entry
          description: Narrows the check to one benefit.
        quantity:
          type: number
          default: 1
          description: Uses to check for, e.g. the member plus guests.
      required:
        - membershipNumber
        - type
    BenefitValidationMembershipTypeDto:
      type: object
      properties:
        id:
          type: string
          description: External identifier for the membership type.
        name:
          type: object
          additionalProperties:
            type: string
      required:
        - id
        - name
    BenefitValidationMemberDto:
      type: object
      properties:
        membershipNumber:
          type: string
          example: "2662667052"
        firstName:
          type: string
          nullable: true
        membershipType:
          $ref: "#/components/schemas/BenefitValidationMembershipTypeDto"
        phase:
          type: string
          enum:
            - TRIAL
            - ACTIVE
            - GRACE
            - EXPIRED
          nullable: true
          description: Null before the membership has started.
        billingStatus:
          type: string
          enum:
            - SETTLED
            - AT_RISK
            - OVERDUE
            - BLOCKED
      required:
        - membershipNumber
        - firstName
        - membershipType
        - phase
        - billingStatus
    BenefitUsageLimitDto:
      type: object
      properties:
        count:
          type: number
          description: Maximum number of uses within the window.
        window:
          type: string
          enum:
            - TOTAL
            - DAY
            - MEMBERSHIP_YEAR
          description: The period the count applies to; MEMBERSHIP_YEAR runs from the anniversary of the membership start date.
        scope:
          type: string
          enum:
            - MEMBER
            - MEMBERSHIP
          description: "Whom the count is shared by: MEMBER counts per person, MEMBERSHIP shares it across the whole household."
      required:
        - count
        - window
        - scope
    BenefitValidationBenefitDto:
      type: object
      properties:
        key:
          type: string
          example: benefit.free_entry
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: Kind of benefit, e.g. ACCESS (entry to a venue or facility) or PRODUCT_DISCOUNT (a discount on purchases).
        name:
          type: object
          additionalProperties:
            type: string
        remaining:
          type: number
          nullable: true
          description: Uses left in the current window; null means unlimited.
        limit:
          nullable: true
          description: The configured usage limit of the benefit, the same for every member; null means unlimited. Unlike remaining, it does not change as uses are recorded.
          type: object
          allOf:
            - $ref: "#/components/schemas/BenefitUsageLimitDto"
        config:
          description: The config of the benefit type; narrow on `type`.
          oneOf:
            - $ref: "#/components/schemas/AccessBenefitConfigDto"
            - $ref: "#/components/schemas/ProductDiscountBenefitConfigDto"
          discriminator:
            propertyName: type
            mapping:
              ACCESS: "#/components/schemas/AccessBenefitConfigDto"
              PRODUCT_DISCOUNT: "#/components/schemas/ProductDiscountBenefitConfigDto"
      required:
        - key
        - type
        - name
        - remaining
        - limit
        - config
    BenefitValidationResponseDto:
      type: object
      properties:
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: The kind of benefit that was checked, e.g. ACCESS or PRODUCT_DISCOUNT.
        result:
          type: string
          enum:
            - ALLOWED
            - MEMBERSHIP_NOT_FOUND
            - MEMBERSHIP_NOT_ENTITLED
            - MEMBERSHIP_BLOCKED
            - NOT_APPLICABLE
            - DENIED_BY_RULE
            - LIMIT_REACHED
          description: ALLOWED when a usage with the same request would be accepted right now, otherwise why it would be refused.
        appliedBenefitKey:
          type: string
          nullable: true
          description: The benefit used for usages that don’t specify a key.
        member:
          $ref: "#/components/schemas/BenefitValidationMemberDto"
        benefits:
          description: The benefits the member may use right now; empty when result is not ALLOWED.
          type: array
          items:
            $ref: "#/components/schemas/BenefitValidationBenefitDto"
      required:
        - type
        - result
        - appliedBenefitKey
        - member
        - benefits
    BenefitUsageContextSchema:
      type: object
      properties:
        locationReference:
          type: string
          description: Where the member was checked, e.g. a gate or till; stored for reporting, not evaluated.
        externalReference:
          type: string
          description: The caller's own reference, e.g. a receipt number.
    RedeemBenefitSchema:
      type: object
      properties:
        membershipNumber:
          type: string
          example: "2662667052"
          description: The membership number on the member's card.
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: "The question asked: may this member enter, or which discount applies."
        benefitKey:
          type: string
          description: The benefit to redeem; without it the best applicable benefit of the type is used.
        quantity:
          type: number
          example: 1
        context:
          $ref: "#/components/schemas/BenefitUsageContextSchema"
      required:
        - membershipNumber
        - type
        - quantity
    BenefitUsageResponseDto:
      type: object
      properties:
        id:
          type: string
          description: External identifier for the use.
        benefitKey:
          type: string
          example: benefit.free_entry
        quantity:
          type: number
        remaining:
          type: number
          nullable: true
          description: Uses left in the current window after this use; null when unlimited, and on a replay or reversal.
        createdAt:
          format: date-time
          type: string
        reversedAt:
          format: date-time
          type: string
          nullable: true
          description: Set once the use has been reversed; a reversed use no longer counts toward the limit.
      required:
        - id
        - benefitKey
        - quantity
        - remaining
        - createdAt
        - reversedAt
    ConsentStateItemDto:
      type: object
      properties:
        definition:
          description: The consent definition.
          allOf:
            - $ref: "#/components/schemas/ConsentDefinitionResponseDto"
        status:
          type: string
          enum:
            - GRANTED
            - WITHDRAWN
          nullable: true
          description: The party's current status, or null when never decided.
        grantedVersion:
          type: number
          nullable: true
          description: The definition version the current decision was captured against, or null.
        recordedAt:
          format: date-time
          type: string
          nullable: true
          description: When the current decision was recorded, or null.
        source:
          type: string
          enum:
            - TILL
            - SHOP
          nullable: true
          description: Where the current decision was captured (till or shop), or null when never decided.
        author:
          type: string
          nullable: true
          description: Display name of the user who recorded the decision (till captures only), or null when unattributed or unresolved.
        outdated:
          type: boolean
          description: True when the party granted an older version and the legal text has since changed.
      required:
        - definition
        - status
        - grantedVersion
        - recordedAt
        - source
        - author
        - outdated
    PartyConsentStateResponseDto:
      type: object
      properties:
        items:
          description: Every definition with the party's current consent state.
          type: array
          items:
            $ref: "#/components/schemas/ConsentStateItemDto"
      required:
        - items
    ConsentGrantSchema:
      type: object
      properties:
        definitionId:
          type: string
          description: Opaque ulid of the consent definition this decision is for.
        granted:
          type: boolean
          description: True to grant the consent, false to withhold/withdraw it.
      required:
        - definitionId
        - granted
    RecordConsentGrantsSchema:
      type: object
      properties:
        partyId:
          type: string
          description: Opaque ulid of the party (data subject) the decisions are for.
        items:
          type: array
          items:
            $ref: "#/components/schemas/ConsentGrantSchema"
      required:
        - partyId
        - items
    PricePlanResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the price plan.
        membershipTypeId:
          type: string
          description: Opaque external identifier of the membership type this plan belongs to.
        name:
          type: object
          additionalProperties:
            type: string
          description: Human-readable price plan name, keyed by language.
        billingMode:
          type: string
          enum:
            - RECURRING
            - SINGLE
          description: Whether the plan bills on a recurring cycle or takes exactly one payment.
        billingCycleLength:
          type: number
          nullable: true
          description: How many `billingCycleUnit`s one billing cycle lasts, or null for a `SINGLE` plan.
          example: 1
        billingCycleUnit:
          type: string
          enum:
            - MONTH
            - YEAR
          nullable: true
          description: The unit `billingCycleLength` is expressed in, or null for a `SINGLE` plan.
        startDate:
          type: string
          format: date
          description: Calendar day the membership period starts.
          example: 2026-01-01
        endDate:
          type: string
          format: date
          nullable: true
          description: Calendar day the membership period ends, or null for an open-ended plan.
          example: 2090-01-01
        active:
          type: boolean
          description: Whether the plan can still be subscribed to.
        createdAt:
          format: date-time
          type: string
          description: When the price plan was created.
        updatedAt:
          format: date-time
          type: string
          description: When the price plan was last updated.
      required:
        - id
        - membershipTypeId
        - name
        - billingMode
        - billingCycleLength
        - billingCycleUnit
        - startDate
        - endDate
        - active
        - createdAt
        - updatedAt
    UpdatePricePlanSchema:
      type: object
      properties:
        name:
          description: Human-readable price plan name per language. Omit to leave it unchanged.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        endDate:
          type: string
          format: date
          description: "Calendar day the membership period ends — must be after the start date and in the future. Only valid on a plan that already has an end date: the period type cannot change."
          example: 2090-01-01
        active:
          type: boolean
          description: Whether the plan can still be subscribed to.
    CreatePricePlanSchema:
      type: object
      properties:
        name:
          description: Human-readable price plan name per language.
          allOf:
            - $ref: "#/components/schemas/TranslatedTextSchema"
        billingMode:
          type: string
          enum:
            - RECURRING
            - SINGLE
          description: Whether the plan bills on a recurring cycle or takes exactly one payment. `SINGLE` plans must omit `billingCycleLength`/`billingCycleUnit`; `RECURRING` plans must supply both.
        billingCycleLength:
          type: number
          minimum: 1
          maximum: 60
          description: How many `billingCycleUnit`s one billing cycle lasts. Required unless `billingMode` is `SINGLE`.
          example: 1
        billingCycleUnit:
          type: string
          enum:
            - MONTH
            - YEAR
          description: The unit `billingCycleLength` is expressed in. Required unless `billingMode` is `SINGLE`.
        startDate:
          type: string
          format: date
          description: Calendar day the membership period starts. Fixed once the plan exists.
          example: 2026-01-01
        endDate:
          type: string
          format: date
          nullable: true
          description: Calendar day the membership period ends. Omit it or send null for an open-ended plan that does not end.
          example: 2090-01-01
      required:
        - name
        - billingMode
        - startDate
    UpdatePriceSchema:
      type: object
      properties:
        vatRateId:
          type: string
          description: Opaque external identifier of the VAT rate that applies to this price.
          example: 01KZTHZMV0DQ2KE4K01XVX33WE
        initialPrice:
          type: number
          minimum: 0
          maximum: 99999999.99
          description: Amount charged for the first billing cycle, including VAT.
          example: 120
        renewalPrice:
          type: number
          minimum: 0
          maximum: 99999999.99
          description: Amount charged for every renewal billing cycle, including VAT.
          example: 100
        validFrom:
          type: string
          format: date
          description: Calendar day this price takes effect. Only changeable while the price has not taken effect yet.
          example: 2026-01-01
    PriceResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the price.
        pricePlanId:
          type: string
          description: Opaque external identifier of the price plan this price belongs to.
        vatRateId:
          type: string
          description: Opaque external identifier of the VAT rate that applies.
        initialPrice:
          type: number
          description: Amount charged for the first billing cycle, including VAT.
          example: 120
        renewalPrice:
          type: number
          description: Amount charged for every renewal billing cycle, including VAT.
          example: 100
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO-4217 alpha-3 currency code.
          example: EUR
        validFrom:
          type: string
          format: date
          description: Calendar day this price takes effect. It applies until the next price on the same plan starts; there is no end date.
          example: 2026-01-01
        createdAt:
          format: date-time
          type: string
          description: When the price was recorded.
        updatedAt:
          format: date-time
          type: string
          description: When the price was last updated.
      required:
        - id
        - pricePlanId
        - vatRateId
        - initialPrice
        - renewalPrice
        - currencyCode
        - validFrom
        - createdAt
        - updatedAt
    CreatePriceSchema:
      type: object
      properties:
        vatRateId:
          type: string
          description: Opaque external identifier of the VAT rate that applies to this price.
          example: 01KZTHZMV0DQ2KE4K01XVX33WE
        initialPrice:
          type: number
          minimum: 0
          maximum: 99999999.99
          description: Amount charged for the first billing cycle, including VAT.
          example: 120
        renewalPrice:
          type: number
          minimum: 0
          maximum: 99999999.99
          description: Amount charged for every renewal billing cycle, including VAT.
          example: 100
        validFrom:
          type: string
          format: date
          description: Calendar day this price takes effect. It applies until the next price on the same plan starts. No other price on the plan may start on the same day.
          example: 2026-01-01
      required:
        - vatRateId
        - initialPrice
        - renewalPrice
        - validFrom
    MembershipConsentGrantSchema:
      type: object
      properties:
        definitionId:
          type: string
          description: Opaque ulid of the consent definition this decision is for.
        granted:
          type: boolean
          description: True to grant the consent, false to withhold it.
      required:
        - definitionId
        - granted
    CreateMembershipSchema:
      type: object
      properties:
        membershipTypeId:
          type: string
          description: Opaque external identifier of the membership type this membership is for.
          example: 01KZTHZMV0DQ2KE4K01XVX33WE
        pricePlanId:
          type: string
          description: Opaque external identifier of the price plan this membership is on.
          example: 01KZTHZMV0DQ2KE4K01XVX33WE
        startDate:
          type: string
          format: date
          description: Calendar day the membership starts.
          example: 2026-01-01
        endDate:
          type: string
          format: date
          description: Calendar day the membership ends, if already known.
          example: 2090-01-01
        ownerPartyId:
          type: string
          description: Opaque external identifier of the party who becomes this membership's owner.
          example: 01KZTHZMV0DQ2KE4K01XVX33WE
        ownerAsMember:
          type: boolean
          description: When true, the owner is also linked as a member, created atomically with the membership.
          example: true
        consents:
          description: Consent decisions captured at the till for the owner. Mandatory consents left ungranted block the sale.
          type: array
          items:
            $ref: "#/components/schemas/MembershipConsentGrantSchema"
      required:
        - membershipTypeId
        - pricePlanId
        - startDate
        - ownerPartyId
    MembershipOwnerDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier of the owning party.
        type:
          type: string
          enum:
            - NATURAL_PERSON
            - ORGANISATION
          description: Whether the owner is a natural person or an organisation.
        name:
          type: string
          description: Display name of the owning party.
      required:
        - id
        - type
        - name
    MembershipResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the membership.
        membershipTypeId:
          type: string
          description: Opaque external identifier of the membership type this membership is for.
        membershipTypeName:
          type: object
          additionalProperties:
            type: string
          description: Name of the membership type this membership is for, keyed by language.
        pricePlanId:
          type: string
          description: Opaque external identifier of the price plan this membership is on.
        pricePlanName:
          type: object
          additionalProperties:
            type: string
          description: Name of the price plan this membership is on, keyed by language.
        owner:
          nullable: true
          description: The party that owns this membership.
          type: object
          allOf:
            - $ref: "#/components/schemas/MembershipOwnerDto"
        status:
          type: string
          enum:
            - DRAFT
            - ACTIVE
            - CANCELLED
            - EXPIRED
          description: Lifecycle status of the membership.
        startDate:
          type: string
          format: date
          description: Calendar day the membership starts.
          example: 2026-01-01
        endDate:
          type: string
          format: date
          nullable: true
          description: Calendar day the membership ends, or null if not yet known.
          example: 2090-01-01
        nextInvoiceDate:
          type: string
          format: date
          nullable: true
          description: Calendar day the next invoice is due, or null if not yet scheduled.
          example: 2026-02-01
        billingStatus:
          type: string
          enum:
            - SETTLED
            - AT_RISK
            - OVERDUE
            - BLOCKED
          description: Billing/dunning status of the membership.
        createdAt:
          format: date-time
          type: string
          description: When the membership was created.
        updatedAt:
          format: date-time
          type: string
          description: When the membership was last updated.
      required:
        - id
        - membershipTypeId
        - membershipTypeName
        - pricePlanId
        - pricePlanName
        - owner
        - status
        - startDate
        - endDate
        - nextInvoiceDate
        - billingStatus
        - createdAt
        - updatedAt
    CancelMembershipSchema:
      type: object
      properties:
        endDate:
          type: string
          format: date
          description: Calendar day the membership is cancelled as of; recorded as its end date.
          example: 2026-06-30
      required:
        - endDate
    PartyMembershipResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the party-membership link.
        partyId:
          type: string
          description: Opaque external identifier of the linked party.
        membershipId:
          type: string
          description: Opaque external identifier of the membership the party is linked to.
        publicId:
          type: string
          description: Public membership number of the link ("26" followed by 8 digits).
        createdAt:
          format: date-time
          type: string
          description: When the link was created.
        updatedAt:
          format: date-time
          type: string
          description: When the link was last updated.
      required:
        - id
        - partyId
        - membershipId
        - publicId
        - createdAt
        - updatedAt
    CreatePartyMembershipSchema:
      type: object
      properties:
        partyId:
          type: string
          description: Opaque external identifier of the party to link to the membership as a member.
          example: 01KZTHZMV0DQ2KE4K01XVX33WE
      required:
        - partyId
    MembershipMemberResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the party-membership link.
        membershipId:
          type: string
          description: Opaque external identifier of the membership the party is linked to.
        publicId:
          type: string
          description: Public membership number of the link ("26" followed by 8 digits).
        createdAt:
          format: date-time
          type: string
          description: When the link was created.
        updatedAt:
          format: date-time
          type: string
          description: When the link was last updated.
        party:
          description: The member linked to the membership.
          allOf:
            - $ref: "#/components/schemas/PartyResponseDto"
      required:
        - id
        - membershipId
        - publicId
        - createdAt
        - updatedAt
        - party
    CreateManualPartyMandateSchema:
      type: object
      properties:
        iban:
          type: string
          description: IBAN the tenant collected from the party. Validated with the MOD-97 checksum.
          example: NL91ABNA0417164300
      required:
        - iban
    PartyMandateResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the party mandate.
        partyId:
          type: string
          description: Opaque external identifier of the party this mandate belongs to.
        provider:
          type: string
          enum:
            - CM_COM_PS
          description: PSP that issued this mandate.
        executionMethod:
          type: string
          enum:
            - CARD_TOKEN
            - SEPA_DIRECT_DEBIT
            - PSP_SUBSCRIPTION_REFERENCE
          description: How we actually collect against this mandate.
        originMethod:
          type: string
          enum:
            - CARD
            - IDEAL
            - SEPA_DIRECT_DEBIT
          description: How the customer originally authorised this mandate.
        revokedAt:
          format: date-time
          type: string
          nullable: true
          description: When this mandate was revoked, or null if still active.
        revokedReason:
          type: string
          nullable: true
          description: Translation key explaining why this mandate was revoked, or null while it is active.
          example: apps.management.relations.detail.payments.mandate.revoked-reason.iban-rejected
        ibanLastDigits:
          type: string
          nullable: true
          description: Last 3 digits of the IBAN, or null unless executionMethod is SEPA_DIRECT_DEBIT.
          example: "300"
        subscriptionReference:
          type: string
          nullable: true
          description: Our reference for the PSP subscription this mandate runs on, as the PSP knows it; null if not provisioned.
          example: 01M4DS178BCZ2Y1P24YV6FCBX1
        createdAt:
          format: date-time
          type: string
          description: When the mandate was created.
        updatedAt:
          format: date-time
          type: string
          description: When the mandate was last updated.
      required:
        - id
        - partyId
        - provider
        - executionMethod
        - originMethod
        - revokedAt
        - revokedReason
        - ibanLastDigits
        - subscriptionReference
        - createdAt
        - updatedAt
    MandateAuthorisationResponseDto:
      type: object
      properties:
        redirectUrl:
          type: string
          description: The payment provider's hosted payment page the party is sent to in order to authorise the new mandate.
          example: https://testsecure.docdatapayments.com/ps/menu?merchant_name=cm-payments_nl&client_language=NL&payment_cluster_key=47484D55E4D02A1C742E37510EFAA127
      required:
        - redirectUrl
    InvoiceResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the invoice.
        membershipId:
          type: string
          description: Opaque external identifier of the membership the invoice belongs to.
        amount:
          type: number
          description: Amount due, VAT included, in the currency below.
        amountExclVat:
          type: number
          description: Amount without VAT, in the currency below. Equals `amount` minus `vatAmount`.
        vatAmount:
          type: number
          description: VAT part of `amount`, in the currency below.
        vatPercentage:
          type: number
          description: VAT percentage applied to this invoice, snapshotted at creation time.
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO currency code the amount is denominated in.
        dueDate:
          type: string
          format: date
          description: Date the invoice is/was due (`YYYY-MM-DD`).
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
            - SUCCEEDED
            - FAILED_RETRYABLE
            - REQUIRES_CUSTOMER_ACTION
            - PENDING_AT_PSP
            - FAILED_FINAL
            - REVERSED
          description: Current status of the invoice.
        retryCount:
          type: number
          description: Number of retry attempts made so far.
        nextRetryAt:
          format: date-time
          type: string
          description: When the next retry is scheduled, if any.
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: When the invoice was created.
        updatedAt:
          format: date-time
          type: string
          description: When the invoice was last updated.
        membershipTypeName:
          type: string
          description: Name of the membership type, snapshotted at creation time.
        pricePlanName:
          type: string
          description: Name of the price plan, snapshotted at creation time.
        billingCycleLength:
          type: number
          description: Billing cycle length, snapshotted at creation time.
          nullable: true
        billingCycleUnit:
          type: string
          enum:
            - MONTH
            - YEAR
          description: Billing cycle unit, snapshotted at creation time.
          nullable: true
        partyName:
          type: string
          description: Name of the party being billed, snapshotted at creation time.
        partyEmail:
          type: string
          description: Email of the party being billed, snapshotted at creation time.
          nullable: true
        billingAttentionLine:
          type: string
          description: Billing address attention line, snapshotted at creation time.
          nullable: true
        billingStreet:
          type: string
          description: Billing address street, snapshotted at creation time.
          nullable: true
        billingHouseNumber:
          type: string
          description: Billing address house number, snapshotted at creation time.
          nullable: true
        billingHouseNumberSuffix:
          type: string
          description: Billing address house number suffix, snapshotted at creation time.
          nullable: true
        billingPostalCode:
          type: string
          description: Billing address postal code, snapshotted at creation time.
          nullable: true
        billingCity:
          type: string
          description: Billing address city, snapshotted at creation time.
          nullable: true
        billingCountryCode:
          type: string
          description: Billing address country code, snapshotted at creation time.
          nullable: true
        organisationName:
          type: string
          description: Name of the organisation being billed, snapshotted at creation time.
      required:
        - id
        - membershipId
        - amount
        - amountExclVat
        - vatAmount
        - vatPercentage
        - currencyCode
        - dueDate
        - status
        - retryCount
        - nextRetryAt
        - createdAt
        - updatedAt
        - membershipTypeName
        - pricePlanName
        - billingCycleLength
        - billingCycleUnit
        - partyName
        - partyEmail
        - billingAttentionLine
        - billingStreet
        - billingHouseNumber
        - billingHouseNumberSuffix
        - billingPostalCode
        - billingCity
        - billingCountryCode
        - organisationName
    PartyMandateSummaryResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the mandate.
        provider:
          type: string
          enum:
            - CM_COM_PS
          description: PSP that holds this mandate.
        executionMethod:
          type: string
          enum:
            - CARD_TOKEN
            - SEPA_DIRECT_DEBIT
            - PSP_SUBSCRIPTION_REFERENCE
          description: How a payment through this mandate is executed.
        originMethod:
          type: string
          enum:
            - CARD
            - IDEAL
            - SEPA_DIRECT_DEBIT
          description: How this mandate was originally collected.
        revokedAt:
          format: date-time
          type: string
          description: When this mandate was revoked, if it has been.
          nullable: true
      required:
        - id
        - provider
        - executionMethod
        - originMethod
        - revokedAt
    PaymentAttemptResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the payment attempt.
        attemptNumber:
          type: number
          description: Retry sequence number, starting at 1.
        provider:
          type: string
          enum:
            - CM_COM_PS
          description: PSP the attempt was submitted to, if it got that far.
          nullable: true
        flowType:
          type: string
          enum:
            - BACKGROUND_RECURRING
            - CUSTOMER_INITIATED_REDIRECT
            - MANUAL_ENTRY
          description: Payment flow used for this attempt.
        providerRef:
          type: string
          description: The PSP's own reference for this attempt, if any, or the admin-entered reference ("kenmerk") for a manual attempt.
          nullable: true
        pspOrderId:
          type: string
          description: The PSP order this attempt was made against, shared across retries of the same invoice.
          nullable: true
        manualMethod:
          type: string
          enum:
            - CASH
            - BANK_TRANSFER
            - OTHER
          description: How a MANUAL_ENTRY attempt was actually paid.
          nullable: true
        originMethod:
          type: string
          enum:
            - CARD
            - IDEAL
            - SEPA_DIRECT_DEBIT
          description: How the customer paid, for a one-off attempt with no mandate on file.
          nullable: true
        status:
          type: string
          enum:
            - SUBMITTED
            - SUCCEEDED
            - FAILED
            - REVERSED
          description: Current status of the attempt.
        initiatedAt:
          format: date-time
          type: string
          description: When the attempt was submitted.
        completedAt:
          format: date-time
          type: string
          description: When the attempt reached a final state, if it has.
          nullable: true
        failureReasonCode:
          type: string
          description: Machine-readable failure reason, if the attempt failed.
          nullable: true
        failureReasonMessage:
          type: string
          description: Human-readable failure reason, if the attempt failed.
          nullable: true
        amount:
          type: number
          description: Amount this attempt was for, in major currency units.
        partyMandate:
          description: The mandate used for this attempt.
          nullable: true
          type: object
          allOf:
            - $ref: "#/components/schemas/PartyMandateSummaryResponseDto"
        createdAt:
          format: date-time
          type: string
          description: When the attempt was created.
        updatedAt:
          format: date-time
          type: string
          description: When the attempt was last updated.
      required:
        - id
        - attemptNumber
        - provider
        - flowType
        - providerRef
        - pspOrderId
        - manualMethod
        - originMethod
        - status
        - initiatedAt
        - completedAt
        - failureReasonCode
        - failureReasonMessage
        - amount
        - partyMandate
        - createdAt
        - updatedAt
    DunningCaseResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the dunning case.
        step:
          type: string
          enum:
            - NOTIFIED
            - REMINDED
            - BLOCKED
          description: Current step of the dunning state machine.
        startedAt:
          format: date-time
          type: string
          description: When the dunning case was opened.
        nextActionAt:
          format: date-time
          type: string
          description: When the next dunning action is due; null when nothing is scheduled.
          nullable: true
        resolvedAt:
          format: date-time
          type: string
          description: When the dunning case was resolved, if it has been.
          nullable: true
        resolution:
          type: string
          enum:
            - AUTO_RETRY
            - CUSTOMER_PAID
            - MANUAL
            - MEMBERSHIP_CANCELLED
          description: How the dunning case was resolved, if it has been.
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: When the dunning case was created.
        updatedAt:
          format: date-time
          type: string
          description: When the dunning case was last updated.
      required:
        - id
        - step
        - startedAt
        - nextActionAt
        - resolvedAt
        - resolution
        - createdAt
        - updatedAt
    InvoiceDetailResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the invoice.
        membershipId:
          type: string
          description: Opaque external identifier of the membership the invoice belongs to.
        amount:
          type: number
          description: Amount due, VAT included, in the currency below.
        amountExclVat:
          type: number
          description: Amount without VAT, in the currency below. Equals `amount` minus `vatAmount`.
        vatAmount:
          type: number
          description: VAT part of `amount`, in the currency below.
        vatPercentage:
          type: number
          description: VAT percentage applied to this invoice, snapshotted at creation time.
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO currency code the amount is denominated in.
        dueDate:
          type: string
          format: date
          description: Date the invoice is/was due (`YYYY-MM-DD`).
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
            - SUCCEEDED
            - FAILED_RETRYABLE
            - REQUIRES_CUSTOMER_ACTION
            - PENDING_AT_PSP
            - FAILED_FINAL
            - REVERSED
          description: Current status of the invoice.
        retryCount:
          type: number
          description: Number of retry attempts made so far.
        nextRetryAt:
          format: date-time
          type: string
          description: When the next retry is scheduled, if any.
          nullable: true
        createdAt:
          format: date-time
          type: string
          description: When the invoice was created.
        updatedAt:
          format: date-time
          type: string
          description: When the invoice was last updated.
        membershipTypeName:
          type: string
          description: Name of the membership type, snapshotted at creation time.
        pricePlanName:
          type: string
          description: Name of the price plan, snapshotted at creation time.
        billingCycleLength:
          type: number
          description: Billing cycle length, snapshotted at creation time.
          nullable: true
        billingCycleUnit:
          type: string
          enum:
            - MONTH
            - YEAR
          description: Billing cycle unit, snapshotted at creation time.
          nullable: true
        partyName:
          type: string
          description: Name of the party being billed, snapshotted at creation time.
        partyEmail:
          type: string
          description: Email of the party being billed, snapshotted at creation time.
          nullable: true
        billingAttentionLine:
          type: string
          description: Billing address attention line, snapshotted at creation time.
          nullable: true
        billingStreet:
          type: string
          description: Billing address street, snapshotted at creation time.
          nullable: true
        billingHouseNumber:
          type: string
          description: Billing address house number, snapshotted at creation time.
          nullable: true
        billingHouseNumberSuffix:
          type: string
          description: Billing address house number suffix, snapshotted at creation time.
          nullable: true
        billingPostalCode:
          type: string
          description: Billing address postal code, snapshotted at creation time.
          nullable: true
        billingCity:
          type: string
          description: Billing address city, snapshotted at creation time.
          nullable: true
        billingCountryCode:
          type: string
          description: Billing address country code, snapshotted at creation time.
          nullable: true
        organisationName:
          type: string
          description: Name of the organisation being billed, snapshotted at creation time.
        attempts:
          description: Every gateway attempt made for this invoice, oldest first.
          type: array
          items:
            $ref: "#/components/schemas/PaymentAttemptResponseDto"
        dunningCase:
          description: The dunning case opened for this invoice, if any.
          nullable: true
          type: object
          allOf:
            - $ref: "#/components/schemas/DunningCaseResponseDto"
      required:
        - id
        - membershipId
        - amount
        - amountExclVat
        - vatAmount
        - vatPercentage
        - currencyCode
        - dueDate
        - status
        - retryCount
        - nextRetryAt
        - createdAt
        - updatedAt
        - membershipTypeName
        - pricePlanName
        - billingCycleLength
        - billingCycleUnit
        - partyName
        - partyEmail
        - billingAttentionLine
        - billingStreet
        - billingHouseNumber
        - billingHouseNumberSuffix
        - billingPostalCode
        - billingCity
        - billingCountryCode
        - organisationName
        - attempts
        - dunningCase
    RegisterPaymentAttemptDto:
      type: object
      properties:
        paidAt:
          type: string
          format: date-time
          description: When the payment was actually made.
        method:
          type: string
          enum:
            - CASH
            - BANK_TRANSFER
            - OTHER
          description: How the payment was made.
        amount:
          type: number
          description: Amount actually collected for this attempt, in major currency units.
        reference:
          type: string
          description: Free-text reference for the payment ("kenmerk"), e.g. a bank transfer reference.
      required:
        - paidAt
        - method
        - amount
    InvoiceListItemDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the invoice.
        amount:
          type: number
          description: Amount due, VAT included, in the currency below.
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO currency code the amount is denominated in.
        dueDate:
          type: string
          format: date
          description: Date the invoice is/was due (`YYYY-MM-DD`).
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
            - SUCCEEDED
            - FAILED_RETRYABLE
            - REQUIRES_CUSTOMER_ACTION
            - PENDING_AT_PSP
            - FAILED_FINAL
            - REVERSED
          description: Current status of the invoice.
        membershipTypeName:
          type: string
          description: Name of the membership type, snapshotted at creation time.
        partyName:
          type: string
          description: Name of the party being billed, snapshotted at creation time.
        holder:
          nullable: true
          description: Current owner of the membership the invoice belongs to.
          type: object
          allOf:
            - $ref: "#/components/schemas/MembershipOwnerDto"
      required:
        - id
        - amount
        - currencyCode
        - dueDate
        - status
        - membershipTypeName
        - partyName
        - holder
    ShopStylingResponseDto:
      type: object
      properties:
        bodyBackgroundColor:
          type: string
          example: "#f4f5f7"
        bodyTextColor:
          type: string
          example: "#1a1a1a"
        cardBackgroundColor:
          type: string
          example: "#ffffff"
        cardTextColor:
          type: string
          example: "#1a1a1a"
        primaryButtonColor:
          type: string
          example: "#0a5cff"
        primaryButtonTextColor:
          type: string
          example: "#ffffff"
        secondaryButtonColor:
          type: string
          example: "#6b7280"
        secondaryButtonTextColor:
          type: string
          example: "#ffffff"
        boxStyle:
          type: string
          enum:
            - SQUARE
            - ROUNDED
            - ROUND
          example: ROUNDED
      required:
        - bodyBackgroundColor
        - bodyTextColor
        - cardBackgroundColor
        - cardTextColor
        - primaryButtonColor
        - primaryButtonTextColor
        - secondaryButtonColor
        - secondaryButtonTextColor
        - boxStyle
    ShopContactResponseDto:
      type: object
      properties:
        email:
          type: string
          nullable: true
          example: info@museum.nl
        phone:
          type: string
          nullable: true
          example: +31 20 123 4567
        websiteUrl:
          type: string
          nullable: true
          example: https://www.museum.nl
        termsUrl:
          type: string
          nullable: true
          example: https://www.museum.nl/voorwaarden
        privacyUrl:
          type: string
          nullable: true
          example: https://www.museum.nl/privacy
      required:
        - email
        - phone
        - websiteUrl
        - termsUrl
        - privacyUrl
    ShopOrganisationResponseDto:
      type: object
      properties:
        name:
          type: string
          description: Human-readable organisation name.
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO-4217 alpha-3 currency code.
          example: EUR
        languageCode:
          type: string
          enum:
            - DE
            - EN
            - ES
            - FR
            - HE
            - NL
          description: CM upper-case language code.
          example: EN
        enabled:
          type: boolean
          description: Whether the shop accepts new checkouts. A disabled shop lists no membership types.
        donationsEnabled:
          type: boolean
          description: Whether the shop accepts donations on top of a membership.
        giftsEnabled:
          type: boolean
          description: Whether the shop offers buying a membership as a gift.
        slug:
          type: string
          nullable: true
          example: my-club
          description: Canonical URL segment of the shop, or null when none is set.
        styling:
          $ref: "#/components/schemas/ShopStylingResponseDto"
        contact:
          $ref: "#/components/schemas/ShopContactResponseDto"
      required:
        - name
        - currencyCode
        - languageCode
        - enabled
        - donationsEnabled
        - giftsEnabled
        - slug
        - styling
        - contact
    ShopPricePlanResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the price plan.
        name:
          type: string
          description: Human-readable price plan name.
        billingMode:
          type: string
          enum:
            - RECURRING
            - SINGLE
          description: Whether the plan bills on a recurring cycle or takes exactly one payment.
        billingCycleLength:
          type: number
          nullable: true
          description: How many `billingCycleUnit`s one billing cycle lasts, or null for a `SINGLE` plan.
        billingCycleUnit:
          type: string
          enum:
            - MONTH
            - YEAR
          nullable: true
          description: The unit `billingCycleLength` is expressed in, or null for a `SINGLE` plan.
        currencyCode:
          type: string
          enum:
            - EUR
            - GBP
          description: ISO-4217 alpha-3 currency code.
          example: EUR
        vatPercentage:
          type: string
          description: VAT rate percentage applied to this plan’s prices, e.g. "21.00".
        initialPriceExclVat:
          type: string
          description: One-off/first-period price, VAT excluded.
        initialPriceInclVat:
          type: string
          description: One-off/first-period price, VAT included.
        renewalPriceExclVat:
          type: string
          description: Recurring renewal price, VAT excluded.
        renewalPriceInclVat:
          type: string
          description: Recurring renewal price, VAT included.
      required:
        - id
        - name
        - billingMode
        - billingCycleLength
        - billingCycleUnit
        - currencyCode
        - vatPercentage
        - initialPriceExclVat
        - initialPriceInclVat
        - renewalPriceExclVat
        - renewalPriceInclVat
    ShopBenefitResponseDto:
      type: object
      properties:
        name:
          type: string
          description: Benefit name in the requested locale, short enough to show as a tag.
        description:
          type: string
          description: Benefit description in the requested locale; empty when none is set. Shown as a tooltip on the tag.
        type:
          type: string
          enum:
            - ACCESS
            - PRODUCT_DISCOUNT
          description: Kind of benefit, e.g. ACCESS (entry to a venue or facility) or PRODUCT_DISCOUNT (a discount on purchases).
      required:
        - name
        - description
        - type
    ShopMembershipTypeResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the membership type.
        name:
          type: string
          description: Human-readable membership type name.
        description:
          type: string
          nullable: true
          description: Optional human-readable description.
        maxMembers:
          type: number
          nullable: true
          description: Maximum number of members; null means unlimited.
        pricePlans:
          description: Sellable price plans for this membership type.
          type: array
          items:
            $ref: "#/components/schemas/ShopPricePlanResponseDto"
        benefits:
          description: Public benefits that come with this membership type; private and inactive benefits are left out.
          type: array
          items:
            $ref: "#/components/schemas/ShopBenefitResponseDto"
      required:
        - id
        - name
        - description
        - maxMembers
        - pricePlans
        - benefits
    ShopConsentResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the consent definition; what a checkout submits as accepted.
        label:
          type: string
          description: Short human-readable consent name.
        legalText:
          type: string
          nullable: true
          description: The wording the buyer agrees to; null when the organisation has not written any.
        mandatory:
          type: boolean
          description: Whether the consent must be accepted before a checkout can be created.
      required:
        - id
        - label
        - legalText
        - mandatory
    ShopCheckoutBuyerAddressSchema:
      type: object
      properties:
        street:
          type: string
          example: Stationsplein
        houseNumber:
          type: string
          example: "9"
        houseNumberSuffix:
          type: string
          example: K
          nullable: true
        postalCode:
          type: string
          example: 4811 BM
        city:
          type: string
          example: Breda
        countryCode:
          type: string
          description: ISO 3166-1 alpha-2 country code.
          example: NL
      required:
        - street
        - houseNumber
        - postalCode
        - city
        - countryCode
    ShopCheckoutBuyerSchema:
      type: object
      properties:
        givenName:
          type: string
          example: Anna
        familyName:
          type: string
          example: de Vries
        email:
          type: string
          example: anna.devries@example.com
        phoneNumber:
          type: string
          example: "+31612345678"
          nullable: true
        dateOfBirth:
          type: string
          format: date
          example: 1990-05-14
          nullable: true
        address:
          $ref: "#/components/schemas/ShopCheckoutBuyerAddressSchema"
      required:
        - givenName
        - familyName
        - email
        - address
    ShopCheckoutMemberSchema:
      type: object
      properties:
        givenName:
          type: string
          example: Bram
        familyName:
          type: string
          example: de Vries
        dateOfBirth:
          type: string
          format: date
          example: 2015-03-02
          nullable: true
      required:
        - givenName
        - familyName
    CreateShopCheckoutSchema:
      type: object
      properties:
        membershipTypeId:
          type: string
          description: Opaque external identifier of the membership type being purchased.
        pricePlanId:
          type: string
          description: Opaque external identifier of the price plan being purchased.
        startDate:
          type: string
          format: date
          description: Calendar day the membership should start.
          example: 2026-10-01
        locale:
          type: string
          enum:
            - nl-NL
            - en-GB
          description: Locale the buyer is checking out in.
        buyer:
          $ref: "#/components/schemas/ShopCheckoutBuyerSchema"
        members:
          description: Extra members beyond the buyer, who becomes the owner.
          type: array
          items:
            $ref: "#/components/schemas/ShopCheckoutMemberSchema"
        acceptedConsents:
          description: Ids of the consent definitions the buyer accepted; must include every mandatory one.
          type: array
          items:
            type: string
        donationAmount:
          type: number
          example: 5
          description: Donation in major currency units, added to the payment amount. Rejected above zero when the shop has donations disabled.
        donationFrequency:
          type: string
          enum:
            - ONE_TIME
            - YEARLY
          description: How often the buyer means to give. Only the first donation is charged; YEARLY is recorded for follow-up. Defaults to ONE_TIME.
      required:
        - membershipTypeId
        - pricePlanId
        - startDate
        - locale
        - buyer
        - members
        - acceptedConsents
    ShopCheckoutResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Opaque external identifier for the checkout.
        status:
          type: string
          enum:
            - PENDING_PAYMENT
            - PAID
            - DECLINED
          description: Current checkout status.
        redirectUrl:
          type: string
          nullable: true
          description: Where to send the buyer to pay, or null once the checkout is no longer pending.
        membershipTypeName:
          type: string
          description: Name of the membership type being purchased.
        pricePlanName:
          type: string
          description: Name of the price plan being purchased.
        startDate:
          type: string
          format: date
          description: Calendar day the membership starts.
        membershipNumber:
          type: string
          nullable: true
          description: Public membership number, set once the checkout is PAID.
      required:
        - id
        - status
        - redirectUrl
        - membershipTypeName
        - pricePlanName
        - startDate
        - membershipNumber
