openapi: 3.0.3
info:
  title: 'BlueRockTEL Platform API'
  description: 'REST API for the BlueRockTEL BSS (Business Support System) platform. Manage customers, establishments, contacts, catalog items, quotes, invoices, payment plans, tickets, phone calls, PBX hosts, training, users and platform utilities. Generated from the published API reference at https://bluerocktel.com/api-documentation.'
  version: 1.0.0
servers:
  -
    url: 'https://your-instance.bluerocktel.net'
tags:
  -
    name: Authentication
    description: 'Authenticate with the BlueRockTEL API using Laravel Passport bearer tokens. Learn how to obtain and use API tokens.'
  -
    name: 'Bank Details and GoCardless Mandates'
    description: 'Manage customer bank details and GoCardless direct debit mandates for automated payment collection.'
  -
    name: Catalog
    description: 'Manage your product and service catalog including items, families, subfamilies, and bundles through the BlueRockTEL API.'
  -
    name: Communication
    description: 'Mass email newsletters, campaigns, and customer satisfaction surveys with branching logic and reply tracking.'
  -
    name: Contacts
    description: 'Create and manage polymorphic contacts belonging to customers or prospects, with phone number lookup for call center integrations.'
  -
    name: 'Customer Files'
    description: 'Manage sales and billing dossiers with initial charges, recurring subscriptions, and licence line items.'
  -
    name: 'Customer Services'
    description: 'List available services, view customer assignments, and sync service sets for individual customers.'
  -
    name: Customers
    description: 'Manage customer records, search by name or account number, and query consumption data via the BlueRockTEL API.'
  -
    name: Documents
    description: 'Upload, manage, and download polymorphic file attachments stored on OVH Swift object storage.'
  -
    name: Establishments
    description: 'Manage legal establishments (branches, subsidiaries) attached to a customer or prospect, each with its own registration number and address.'
  -
    name: 'Hosts and Infrastructure'
    description: 'Manage server inventory with customer filtering, software assignments, network interfaces, and reference data for infrastructure management.'
  -
    name: Incidents
    description: 'Access public platform incident notices with status updates for real-time outage and degradation monitoring.'
  -
    name: 'Incoming Mails'
    description: 'Process incoming email messages, convert them to support tickets, and handle Mailgun webhook delivery events.'
  -
    name: 'Information Collections'
    description: 'Manage onboarding forms for technical data collection including PBX configuration, with customer-facing token access.'
  -
    name: Invoices
    description: 'Access final, preprod, and proforma invoices. Download PDF content and publish proforma invoices through the API.'
  -
    name: Notes
    description: 'Attach polymorphic notes to any entity with automatic user attribution and timestamp management.'
  -
    name: 'Organisations and Brands'
    description: 'Retrieve and update top-level tenant organisations and manage brand settings including logos and invoice images.'
  -
    name: 'PBX (3CX)'
    description: 'Manage 3CX phone system integrations including hosts, origins, call records, call logs, and audit trails.'
  -
    name: 'Payment Plans'
    description: 'Create and manage polymorphic payment plans with automatic IBAN mandate generation and audit history.'
  -
    name: 'Phone Calls'
    description: 'Log and manage phone interactions with automatic note creation, ticket activity tracking, and call status management.'
  -
    name: Prospects
    description: 'Manage sales prospects with auto-generated account numbers, full-text search, and prospect-to-customer conversion.'
  -
    name: 'Quotes & Documents'
    description: 'Generate, send, and electronically sign commercial documents including quotes, proformas, and contracts.'
  -
    name: Recruitment
    description: 'Manage candidates, job openings, interviews, contracts, and employer details for internal HR and staffing use cases.'
  -
    name: 'Sales (Proforma Lines and Credits)'
    description: 'Manage proforma sales lines with automatic price calculation from catalog items, including credit applications.'
  -
    name: Tasks
    description: 'List active tasks assigned to the authenticated user for action tracking and follow-up management.'
  -
    name: Tickets
    description: 'Full REST API for ticket lifecycle management, replies, time tracking, support contracts, teams, and analytics.'
  -
    name: Training
    description: 'Learning management system (LMS) covering courses, programs, lessons, materials, quizzes, and student enrollment management.'
  -
    name: 'Users and Teams'
    description: 'Manage user accounts with role-based filtering, team assignments, and admin impersonation capabilities.'
  -
    name: 'Utilities and Helpers'
    description: 'Access helper endpoints for version checks, reference data, string formatting, quick search, and system configuration.'
components:
  securitySchemes:
    default:
      type: http
      scheme: bearer
      description: 'The BlueRockTEL platform issues bearer tokens through <a href="https://laravel.com/docs/passport">Laravel Passport</a>. Obtain a token by posting your email and password to <code>POST /api/login</code>, then send it on every subsequent request in the <code>Authorization: Bearer {token}</code> header.'
security:
  -
    default: []
paths:
  /api/login:
    post:
      summary: 'Authenticate with email and password.'
      operationId: authenticateWithEmailAndPassword
      description: 'Authenticate with email and password. Returns an API bearer token.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
                example: "{\n  \"token\": \"...\",\n  \"user\": { \"id\": 1, \"name\": \"...\", \"email\": \"...\", ... },\n  \"teams\": [...],\n  \"support_teams\": [...]\n}"
        401:
          description: 'Invalid credentials.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Authentication
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: 'User email address'
                  example: string
                password:
                  type: string
                  description: 'User password'
                  example: string
              required:
                - email
                - password
      security: []
  '/api/brtel-login/{token}':
    get:
      summary: 'Internal BlueRockTEL login via pre-existing API token.'
      operationId: internalBlueRockTELLoginViaPreExistingAPIToken
      description: 'Internal BlueRockTEL login via pre-existing API token. Only available from whitelisted IP addresses.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  token: ...
                properties:
                  token:
                    type: string
                    example: ...
        403:
          description: 'IP not whitelisted.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Token not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Authentication
      security: []
    parameters:
      -
        in: path
        name: token
        description: "The user's `api_token` field value"
        example: string
        required: true
        schema:
          type: string
  /api/user:
    get:
      summary: 'Get the currently authenticated user.'
      operationId: getTheCurrentlyAuthenticatedUser
      description: 'Get the currently authenticated user.'
      parameters: []
      responses:
        200:
          description: 'User object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Authentication
  /api/v1/bank-details:
    get:
      summary: 'List bank details for a specific fileable entity.'
      operationId: listBankDetailsForASpecificFileableEntity
      description: 'List bank details for a specific fileable entity.'
      parameters: []
      responses:
        200:
          description: 'Bank detail object for the fileable (or `null` if none).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Fileable entity not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    post:
      summary: 'Create a new bank detail record.'
      operationId: createANewBankDetailRecord
      description: 'Create a new bank detail record.'
      parameters:
        -
          in: query
          name: fileable_type
          description: 'PHP class name'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        201:
          description: 'Created (or updated) bank detail object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: 'Server error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                dt_of_sgntr:
                  type: string
                  description: 'Mandate signature date (format `d/m/Y`). Defaults to today.'
                  example: string
                mandate_identification:
                  type: string
                  description: 'Mandate ID. Auto-generated if not provided.'
                  example: string
  '/api/v1/bank-details/{id}':
    get:
      summary: 'Get a single bank detail by ID.'
      operationId: getASingleBankDetailByID
      description: 'Get a single bank detail by ID.'
      parameters: []
      responses:
        200:
          description: 'Bank detail object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    put:
      summary: 'Update a bank detail record.'
      operationId: updateABankDetailRecord
      description: 'Update a bank detail record.'
      parameters: []
      responses:
        201:
          description: 'Updated bank detail object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    delete:
      summary: 'Delete a bank detail record.'
      operationId: deleteABankDetailRecord
      description: 'Delete a bank detail record.'
      parameters: []
      responses:
        200:
          description: '`{ "message": "element deleted" }`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    parameters:
      -
        in: path
        name: id
        description: 'Bank detail ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/go-cardless-mandates:
    get:
      summary: 'List GoCardless mandates for a customer.'
      operationId: listGoCardlessMandatesForACustomer
      description: 'List GoCardless mandates for a customer.'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Customer ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer ID'
            example: 1
      responses:
        200:
          description: 'Array of mandate objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Customer not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    post:
      summary: 'Create a new GoCardless mandate for a customer.'
      operationId: createANewGoCardlessMandateForACustomer
      description: 'Create a new GoCardless mandate for a customer.'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Customer ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer ID'
            example: 1
      responses:
        201:
          description: 'Created mandate object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Customer not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
  '/api/v1/go-cardless-mandates/{id}':
    get:
      summary: 'Get a single GoCardless mandate by ID.'
      operationId: getASingleGoCardlessMandateByID
      description: 'Get a single GoCardless mandate by ID.'
      parameters: []
      responses:
        200:
          description: 'Mandate object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    put:
      summary: 'Update a GoCardless mandate.'
      operationId: updateAGoCardlessMandate
      description: 'Update a GoCardless mandate.'
      parameters: []
      responses:
        201:
          description: 'Updated mandate object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    delete:
      summary: 'Delete a GoCardless mandate.'
      operationId: deleteAGoCardlessMandate
      description: 'Delete a GoCardless mandate.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Bank Details and GoCardless Mandates'
    parameters:
      -
        in: path
        name: id
        description: 'Mandate ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/items:
    get:
      summary: 'List items with filtering, sorting, and pagination.'
      operationId: listItemsWithFilteringSortingAndPagination
      description: 'List items with filtering, sorting, and pagination. Non-admin/tech users see only active catalog items.'
      parameters:
        -
          in: query
          name: 'filter[description]'
          description: 'Filter by description'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by description'
            example: string
        -
          in: query
          name: 'filter[brand]'
          description: 'Filter by brand'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by brand'
            example: string
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by exact ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by exact ID'
            example: 1
        -
          in: query
          name: 'filter[family_id]'
          description: 'Filter by family'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by family'
            example: 1
        -
          in: query
          name: 'filter[subfamily_id]'
          description: 'Filter by subfamily'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by subfamily'
            example: 1
        -
          in: query
          name: 'filter[internal_reference]'
          description: 'Filter by internal reference'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by internal reference'
            example: string
        -
          in: query
          name: 'filter[active_for_initial_billing]'
          description: 'Filter items active for initial billing'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter items active for initial billing'
            example: true
        -
          in: query
          name: sort
          description: 'Sort field'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field'
            example: string
        -
          in: query
          name: include
          description: 'Relations to include'
          example: string
          required: false
          schema:
            type: string
            description: 'Relations to include'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 10)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 10)'
            example: 1
      responses:
        200:
          description: 'Paginated list of item objects with `family`, `subfamily`, `tax` relations and counts.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    post:
      summary: 'Create a new catalog item.'
      operationId: createANewCatalogItem
      description: 'Create a new catalog item. Requires `create` permission on `Item`.'
      parameters: []
      responses:
        201:
          description: 'Created item object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        403:
          description: 'Insufficient permissions.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
  '/api/v1/items/{id}':
    get:
      summary: 'Get a single item with `family`, `subfamily`, `tax` relations.'
      operationId: getASingleItemWithfamilysubfamilytaxRelations
      description: "Get a single item with `family`, `subfamily`, `tax` relations. The response also embeds the item's `selling_prices` and `supplier_prices`."
      parameters: []
      responses:
        200:
          description: 'Item object, e.g.:'
          content:
            application/json:
              schema:
                type: object
                example:
                  id: 42
                  description: 'Fibre 1Gbps'
                  selling_price_without_tax: '49.99'
                  selling_prices:
                    -
                      id: 1
                      item_id: 42
                      currency: EUR
                      selling_price: '49.99'
                  supplier_prices:
                    -
                      id: 1
                      item_id: 42
                      supplier_id: 7
                      currency: EUR
                      buying_price: '29.99'
                      supplier_ref: null
                      is_default: true
                properties:
                  id:
                    type: integer
                    example: 42
                  description:
                    type: string
                    example: 'Fibre 1Gbps'
                  selling_price_without_tax:
                    type: string
                    example: '49.99'
                  selling_prices:
                    type: array
                    example:
                      -
                        id: 1
                        item_id: 42
                        currency: EUR
                        selling_price: '49.99'
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 1
                        item_id:
                          type: integer
                          example: 42
                        currency:
                          type: string
                          example: EUR
                        selling_price:
                          type: string
                          example: '49.99'
                  supplier_prices:
                    type: array
                    example:
                      -
                        id: 1
                        item_id: 42
                        supplier_id: 7
                        currency: EUR
                        buying_price: '29.99'
                        supplier_ref: null
                        is_default: true
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          example: 1
                        item_id:
                          type: integer
                          example: 42
                        supplier_id:
                          type: integer
                          example: 7
                        currency:
                          type: string
                          example: EUR
                        buying_price:
                          type: string
                          example: '29.99'
                        supplier_ref:
                          type: string
                          example: null
                          nullable: true
                        is_default:
                          type: boolean
                          example: true
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    put:
      summary: 'Update a catalog item.'
      operationId: updateACatalogItem
      description: 'Update a catalog item.'
      parameters: []
      responses:
        201:
          description: 'Updated item object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    delete:
      summary: 'Delete a catalog item.'
      operationId: deleteACatalogItem
      description: 'Delete a catalog item. Fails if the item is used in any customer file initials, recurrings, or licences.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Item is in use.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    parameters:
      -
        in: path
        name: id
        description: 'Item ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/items/internal_reference/{internal_reference}':
    get:
      summary: 'Find an item by its internal reference code.'
      operationId: findAnItemByItsInternalReferenceCode
      description: 'Find an item by its internal reference code.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  item_id: 42
                properties:
                  item_id:
                    type: integer
                    example: 42
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    parameters:
      -
        in: path
        name: internal_reference
        description: 'Internal reference string'
        example: string
        required: true
        schema:
          type: string
  /api/v1/item-selling-prices:
    get:
      summary: 'List item selling prices with filtering, sorting, and pagination.'
      operationId: listItemSellingPricesWithFilteringSortingAndPagination
      description: 'List item selling prices with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[item_id]'
          description: 'Filter by item'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by item'
            example: 1
        -
          in: query
          name: 'filter[currency]'
          description: 'Filter by currency'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by currency'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field'
            example: string
        -
          in: query
          name: include
          description: 'Relations to include (`item`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Relations to include (`item`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 10)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 10)'
            example: 1
      responses:
        200:
          description: 'Paginated list of item selling price objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    post:
      summary: 'Create (or update, if the `item_id`/`currency` pair already exists) a selling price for an item.'
      operationId: createorUpdateIfTheitemIdcurrencyPairAlreadyExistsASellingPriceForAnItem
      description: 'Create (or update, if the `item_id`/`currency` pair already exists) a selling price for an item.'
      parameters: []
      responses:
        201:
          description: 'Created (or updated) item selling price object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
  '/api/v1/item-selling-prices/{id}':
    get:
      summary: 'Get a single item selling price.'
      operationId: getASingleItemSellingPrice
      description: 'Get a single item selling price.'
      parameters: []
      responses:
        200:
          description: 'Item selling price object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    put:
      summary: 'Update an item selling price.'
      operationId: updateAnItemSellingPrice
      description: 'Update an item selling price.'
      parameters: []
      responses:
        201:
          description: 'Updated item selling price object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    delete:
      summary: 'Delete an item selling price.'
      operationId: deleteAnItemSellingPrice
      description: 'Delete an item selling price.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    parameters:
      -
        in: path
        name: id
        description: 'Item selling price ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/item-supplier-prices:
    get:
      summary: 'List item supplier prices with filtering, sorting, and pagination.'
      operationId: listItemSupplierPricesWithFilteringSortingAndPagination
      description: 'List item supplier prices with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[item_id]'
          description: 'Filter by item'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by item'
            example: 1
        -
          in: query
          name: 'filter[supplier_id]'
          description: 'Filter by supplier'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by supplier'
            example: 1
        -
          in: query
          name: 'filter[currency]'
          description: 'Filter by currency'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by currency'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field'
            example: string
        -
          in: query
          name: include
          description: 'Relations to include (`item`, `supplier`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Relations to include (`item`, `supplier`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 10)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 10)'
            example: 1
      responses:
        200:
          description: 'Paginated list of item supplier price objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    post:
      summary: 'Create (or update, if the `item_id`/`supplier_id`/`currency` triple already exists) a supplier price for an item.'
      operationId: createorUpdateIfTheitemIdsupplierIdcurrencyTripleAlreadyExistsASupplierPriceForAnItem
      description: 'Create (or update, if the `item_id`/`supplier_id`/`currency` triple already exists) a supplier price for an item. A supplier ID must first be obtained via `GET /v1/suppliers` — suppliers are read-only via the API. Setting `is_default: true` automatically unsets `is_default` on every other supplier price for the same item; only one supplier price per item can be default.'
      parameters: []
      responses:
        201:
          description: 'Created (or updated) item supplier price object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
  '/api/v1/item-supplier-prices/{id}':
    get:
      summary: 'Get a single item supplier price.'
      operationId: getASingleItemSupplierPrice
      description: 'Get a single item supplier price.'
      parameters: []
      responses:
        200:
          description: 'Item supplier price object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    put:
      summary: 'Update an item supplier price.'
      operationId: updateAnItemSupplierPrice
      description: 'Update an item supplier price. Setting `is_default: true` automatically unsets `is_default` on every other supplier price for the same item.'
      parameters: []
      responses:
        201:
          description: 'Updated item supplier price object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    delete:
      summary: 'Delete an item supplier price.'
      operationId: deleteAnItemSupplierPrice
      description: 'Delete an item supplier price.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    parameters:
      -
        in: path
        name: id
        description: 'Item supplier price ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/bundles:
    get:
      summary: 'List all bundles with item counts (initial, licence, recurring).'
      operationId: listAllBundlesWithItemCountsinitialLicenceRecurring
      description: 'List all bundles with item counts (initial, licence, recurring).'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter by name'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by name'
            example: string
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by exact ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by exact ID'
            example: 1
        -
          in: query
          name: sort
          description: 'Sort field'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field'
            example: string
        -
          in: query
          name: include
          description: 'Relations (`items_initial`, `items_licence`, `items_recurring`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Relations (`items_initial`, `items_licence`, `items_recurring`)'
            example: string
      responses:
        200:
          description: 'Array of bundle objects with item counts.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    post:
      summary: 'Create a new bundle.'
      operationId: createANewBundle
      description: 'Create a new bundle. `user_id` is auto-set to authenticated user. Requires `create` permission on `Item`.'
      parameters: []
      responses:
        201:
          description: 'Created bundle object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
  '/api/v1/bundles/{id}':
    get:
      summary: 'Get a single bundle with calculated price sums for each item type (initial, licence, recurring).'
      operationId: getASingleBundleWithCalculatedPriceSumsForEachItemTypeinitialLicenceRecurring
      description: 'Get a single bundle with calculated price sums for each item type (initial, licence, recurring).'
      parameters: []
      responses:
        200:
          description: 'Bundle object with price summaries.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    put:
      summary: 'Update a bundle.'
      operationId: updateABundle
      description: 'Update a bundle.'
      parameters: []
      responses:
        201:
          description: 'Updated bundle object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    delete:
      summary: 'Delete a bundle.'
      operationId: deleteABundle
      description: 'Delete a bundle.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
    parameters:
      -
        in: path
        name: id
        description: 'Bundle ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/suppliers:
    get:
      summary: 'List all suppliers.'
      operationId: listAllSuppliers
      description: 'List all suppliers. Read-only via API.'
      parameters: []
      responses:
        200:
          description: 'Array of supplier objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Catalog
  /api/v1/newsletters:
    get:
      summary: 'List all newsletters.'
      operationId: listAllNewsletters
      description: 'List all newsletters.'
      parameters: []
      responses:
        200:
          description: 'Array of newsletter objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    post:
      summary: 'Create a new newsletter.'
      operationId: createANewNewsletter
      description: 'Create a new newsletter.'
      parameters: []
      responses:
        201:
          description: 'Created newsletter object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  '/api/v1/newsletters/{id}':
    get:
      summary: 'Show a single newsletter.'
      operationId: showASingleNewsletter
      description: 'Show a single newsletter.'
      parameters: []
      responses:
        200:
          description: 'Newsletter object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    put:
      summary: 'Update a newsletter.'
      operationId: updateANewsletter
      description: 'Update a newsletter.'
      parameters: []
      responses:
        200:
          description: 'Updated newsletter object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    delete:
      summary: 'Delete a newsletter.'
      operationId: deleteANewsletter
      description: 'Delete a newsletter.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/newsletters/{id}/test':
    post:
      summary: 'Send a test email for the newsletter to the authenticated user and record the send.'
      operationId: sendATestEmailForTheNewsletterToTheAuthenticatedUserAndRecordTheSend
      description: 'Send a test email for the newsletter to the authenticated user and record the send.'
      parameters: []
      responses:
        201:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/newsletter-lists:
    get:
      summary: 'List all newsletter lists with their contacts.'
      operationId: listAllNewsletterListsWithTheirContacts
      description: 'List all newsletter lists with their contacts.'
      parameters: []
      responses:
        200:
          description: 'Array of list objects with `contacts` (including related data).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    post:
      summary: 'Create a newsletter list.'
      operationId: createANewsletterList
      description: 'Create a newsletter list.'
      parameters: []
      responses:
        201:
          description: 'Created list object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  '/api/v1/newsletter-lists/{id}':
    get:
      summary: 'Show a newsletter list with contacts.'
      operationId: showANewsletterListWithContacts
      description: 'Show a newsletter list with contacts.'
      parameters: []
      responses:
        200:
          description: 'List object with contacts.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    put:
      summary: 'Update a newsletter list and sync its contacts.'
      operationId: updateANewsletterListAndSyncItsContacts
      description: 'Update a newsletter list and sync its contacts.'
      parameters: []
      responses:
        200:
          description: 'Updated list object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    delete:
      summary: 'Delete a newsletter list.'
      operationId: deleteANewsletterList
      description: 'Delete a newsletter list.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/newsletter-campaigns:
    get:
      summary: 'List all campaigns.'
      operationId: listAllCampaigns
      description: 'List all campaigns.'
      parameters: []
      responses:
        200:
          description: 'Array of campaign objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    post:
      summary: 'Create a campaign.'
      operationId: createACampaign
      description: 'Create a campaign.'
      parameters: []
      responses:
        201:
          description: 'Created campaign object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  '/api/v1/newsletter-campaigns/{id}':
    get:
      summary: 'Show a campaign.'
      operationId: showACampaign
      description: 'Show a campaign.'
      parameters: []
      responses:
        200:
          description: 'Campaign object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    put:
      summary: 'Update a campaign.'
      operationId: updateACampaign
      description: 'Update a campaign.'
      parameters: []
      responses:
        200:
          description: 'Updated campaign object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    delete:
      summary: 'Delete a campaign.'
      operationId: deleteACampaign
      description: 'Delete a campaign.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/surveys:
    get:
      summary: 'List all surveys (lightweight resource).'
      operationId: listAllSurveyslightweightResource
      description: 'List all surveys (lightweight resource).'
      parameters: []
      responses:
        200:
          description: 'Array of survey objects (light format, without nested branches).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    post:
      summary: 'Create a survey.'
      operationId: createASurvey
      description: 'Create a survey. A default main branch is automatically created.'
      parameters: []
      responses:
        201:
          description: 'Created survey object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                subject:
                  type: string
                  description: 'Survey subject (min 3, max 255 chars)'
                  example: string
                active:
                  type: boolean
                  description: 'Whether to activate the survey'
                  example: true
              required:
                - subject
  '/api/v1/surveys/{id}':
    get:
      summary: 'Show a survey with branches.'
      operationId: showASurveyWithBranches
      description: 'Show a survey with branches.'
      parameters: []
      responses:
        200:
          description: 'Survey object with branches.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    put:
      summary: 'Update a survey.'
      operationId: updateASurvey
      description: 'Update a survey.'
      parameters: []
      responses:
        200:
          description: 'Updated survey object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    delete:
      summary: 'Delete a survey.'
      operationId: deleteASurvey
      description: 'Delete a survey.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/survey-branches:
    get:
      summary: 'List branches, optionally filtered by survey.'
      operationId: listBranchesOptionallyFilteredBySurvey
      description: 'List branches, optionally filtered by survey.'
      parameters:
        -
          in: query
          name: survey_id
          description: 'Filter by survey ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by survey ID'
            example: 1
      responses:
        200:
          description: 'Array of branch objects with groups.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  /api/v1/survey-questions/types:
    get:
      summary: 'Get the list of available question types.'
      operationId: getTheListOfAvailableQuestionTypes
      description: 'Get the list of available question types.'
      parameters: []
      responses:
        200:
          description: 'Array of question type strings.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  /api/v1/survey-questions:
    get:
      summary: 'List questions, optionally filtered by `group_id`.'
      operationId: listQuestionsOptionallyFilteredBygroupId
      description: 'List questions, optionally filtered by `group_id`.'
      parameters: []
      responses:
        200:
          description: 'Array of question objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    post:
      summary: 'Create a question.'
      operationId: createAQuestion
      description: 'Create a question. Validates the `type` against allowed question types.'
      parameters: []
      responses: {  }
      tags:
        - Communication
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  description: 'Question type (must be in the allowed types list)'
                  example: string
                label:
                  type: string
                  description: 'Question text'
                  example: string
                group_id:
                  type: integer
                  description: 'FK to survey group'
                  example: 1
              required:
                - type
                - label
                - group_id
  /api/v1/survey-reply-requests:
    get:
      summary: 'List reply requests with QueryBuilder filtering and sorting.'
      operationId: listReplyRequestsWithQueryBuilderFilteringAndSorting
      description: 'List reply requests with QueryBuilder filtering and sorting.'
      parameters:
        -
          in: query
          name: 'filter[score]'
          description: 'Filter by NPS score'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by NPS score'
            example: string
        -
          in: query
          name: 'filter[replied]'
          description: 'Filter by whether the contact has replied'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter by whether the contact has replied'
            example: true
        -
          in: query
          name: 'filter[tries]'
          description: 'Filter by number of send attempts'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by number of send attempts'
            example: 1
        -
          in: query
          name: 'filter[created_at_from]'
          description: 'Filter records created after this date'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter records created after this date'
            example: string
        -
          in: query
          name: 'filter[created_at_to]'
          description: 'Filter records created before this date'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter records created before this date'
            example: string
        -
          in: query
          name: 'filter[updated_at_from]'
          description: 'Filter records updated after this date'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter records updated after this date'
            example: string
        -
          in: query
          name: 'filter[updated_at_to]'
          description: 'Filter records updated before this date'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter records updated before this date'
            example: string
      responses:
        200:
          description: 'Paginated list of reply request objects with polymorphic surveyable relationships (Contact, CustomerFile, Ticket).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  /api/v1/survey-reply-requests/customers:
    get:
      summary: 'List unique customers who have associated survey reply requests.'
      operationId: listUniqueCustomersWhoHaveAssociatedSurveyReplyRequests
      description: 'List unique customers who have associated survey reply requests.'
      parameters: []
      responses:
        200:
          description: 'Array of customer objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  /api/v1/survey-reply-requests/export:
    get:
      summary: 'Export survey reply requests to an Excel file.'
      operationId: exportSurveyReplyRequestsToAnExcelFile
      description: 'Export survey reply requests to an Excel file.'
      parameters: []
      responses:
        200:
          description: 'Excel file download.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
  '/api/v1/survey-reply-requests/{id}':
    get:
      summary: 'Show a single reply request.'
      operationId: showASingleReplyRequest
      description: 'Show a single reply request.'
      parameters: []
      responses:
        200:
          description: 'Reply request object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Communication
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/contacts:
    get:
      summary: 'List contacts for a specific entity (customer or prospect).'
      operationId: listContactsForASpecificEntitycustomerOrProspect
      description: 'List contacts for a specific entity (customer or prospect). Results are ordered by `last_name`, `first_name`.'
      parameters:
        -
          in: query
          name: contactable_type
          description: 'PHP class name (e.g. `App\Customer`, `App\Prospect`)'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name (e.g. `App\Customer`, `App\Prospect`)'
            example: string
        -
          in: query
          name: contactable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        200:
          description: 'Array of contact objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
    post:
      summary: 'Create a new contact for an entity.'
      operationId: createANewContactForAnEntity
      description: 'Create a new contact for an entity. Sets `sync_needed = true` and triggers reindexing of the contactable.'
      parameters:
        -
          in: query
          name: contactable_type
          description: 'PHP class name'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name'
            example: string
        -
          in: query
          name: contactable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        201:
          description: 'Created contact object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
  '/api/v1/contacts/{id}':
    get:
      summary: 'Get a single contact with company info, `customer_account`, client permissions, and contactable metadata.'
      operationId: getASingleContactWithCompanyInfocustomerAccountClientPermissionsAndContactableMetadata
      description: 'Get a single contact with company info, `customer_account`, client permissions, and contactable metadata.'
      parameters: []
      responses:
        200:
          description: 'Contact object with permissions.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
    put:
      summary: 'Update a contact.'
      operationId: updateAContact
      description: 'Update a contact. If a client account is linked, permissions are synced. Triggers contactable reindexing.'
      parameters: []
      responses:
        201:
          description: 'Updated contact object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
    delete:
      summary: 'Delete a contact.'
      operationId: deleteAContact
      description: 'Delete a contact.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
    parameters:
      -
        in: path
        name: id
        description: 'Contact ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/contacts/numbers:
    get:
      summary: 'Find a contact by phone number.'
      operationId: findAContactByPhoneNumber
      description: 'Find a contact by phone number. Used by 3CX call flow integrations.'
      parameters:
        -
          in: query
          name: phone_number
          description: 'Phone number to search'
          example: string
          required: true
          schema:
            type: string
            description: 'Phone number to search'
            example: string
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
                example: "{\n  \"data\": { ...contact object... }\n}"
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
  '/api/v1/contacts/{id}/accounts':
    get:
      summary: 'Find all accounts (customers or prospects) sharing the same email address as this contact.'
      operationId: findAllAccountscustomersOrProspectsSharingTheSameEmailAddressAsThisContact
      description: 'Find all accounts (customers or prospects) sharing the same email address as this contact.'
      parameters: []
      responses:
        200:
          description: 'Array of account objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
    parameters:
      -
        in: path
        name: id
        description: 'Contact ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/contacts/{id}/coworkers':
    get:
      summary: 'Get all contacts at the same organization as the given contact (same company/domain).'
      operationId: getAllContactsAtTheSameOrganizationAsTheGivenContactsameCompanydomain
      description: 'Get all contacts at the same organization as the given contact (same company/domain).'
      parameters: []
      responses:
        200:
          description: 'Array of contact objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Contacts
    parameters:
      -
        in: path
        name: id
        description: 'Contact ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/customer-files:
    get:
      summary: 'List all files for a given fileable entity (customer or prospect).'
      operationId: listAllFilesForAGivenFileableEntitycustomerOrProspect
      description: 'List all files for a given fileable entity (customer or prospect).'
      parameters:
        -
          in: query
          name: fileable_type
          description: '`prospect` or `customer`'
          example: string
          required: true
          schema:
            type: string
            description: '`prospect` or `customer`'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        200:
          description: 'Array of customer file objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    post:
      summary: 'Create a new customer file.'
      operationId: createANewCustomerFile
      description: 'Create a new customer file. Auto-sets `fileable_type`, `fileable_id`, and `user_id`. Optionally syncs `contact_ids`.'
      parameters:
        -
          in: query
          name: fileable_type
          description: '`prospect` or `customer`'
          example: string
          required: true
          schema:
            type: string
            description: '`prospect` or `customer`'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        201:
          description: 'Created file object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
  '/api/v1/customer-files/{id}':
    get:
      summary: 'Get a single customer file with all relations: `fileable`, `licences`, `initial`, `recurring`, `telephony lines`, `assig'
      operationId: getASingleCustomerFileWithAllRelationsfileablelicencesinitialrecurringtelephonyLinesassig
      description: 'Get a single customer file with all relations: `fileable`, `licences`, `initial`, `recurring`, `telephony lines`, `assigned users`, `contacts`.'
      parameters: []
      responses:
        200:
          description: 'Customer file with full relations.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    put:
      summary: 'Update a customer file.'
      operationId: updateACustomerFile
      description: 'Update a customer file. Syncs `contact_ids` if provided. Sets `user_id` to current user.'
      parameters: []
      responses:
        201:
          description: 'Updated file object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    delete:
      summary: 'Delete a customer file.'
      operationId: deleteACustomerFile
      description: 'Delete a customer file.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    parameters:
      -
        in: path
        name: id
        description: 'Customer file ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/customer-files/{id}/resources':
    get:
      summary: 'Get all phone line resources for a customer file (from recurring items and telephony lines).'
      operationId: getAllPhoneLineResourcesForACustomerFilefromRecurringItemsAndTelephonyLines
      description: 'Get all phone line resources for a customer file (from recurring items and telephony lines).'
      parameters: []
      responses:
        200:
          description: 'Array of phone line objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    parameters:
      -
        in: path
        name: id
        description: 'Customer file ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/fileables/number/{number}':
    get:
      summary: 'Find a customer file by phone number.'
      operationId: findACustomerFileByPhoneNumber
      description: 'Find a customer file by phone number.'
      parameters: []
      responses:
        200:
          description: 'Customer file object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    parameters:
      -
        in: path
        name: number
        description: 'Phone number'
        example: string
        required: true
        schema:
          type: string
  /api/v1/customer-files/initials:
    get:
      summary: 'List initial items with filtering, sorting, and pagination (Spatie QueryBuilder).'
      operationId: listInitialItemsWithFilteringSortingAndPaginationSpatieQueryBuilder
      description: 'List initial items with filtering, sorting, and pagination (Spatie QueryBuilder).'
      parameters:
        -
          in: query
          name: 'filter[label]'
          description: 'Filter by label'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by label'
            example: string
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by ID'
            example: 1
        -
          in: query
          name: 'filter[customer_file_id]'
          description: 'Filter by customer file'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer file'
            example: 1
        -
          in: query
          name: 'filter[item_id]'
          description: 'Filter by catalog item'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by catalog item'
            example: 1
        -
          in: query
          name: 'filter[invoice_id]'
          description: 'Filter by invoice'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by invoice'
            example: 1
        -
          in: query
          name: 'filter[line_key]'
          description: 'Filter by line key'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by line key'
            example: string
        -
          in: query
          name: 'filter[paused]'
          description: 'Filter paused items'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter paused items'
            example: true
        -
          in: query
          name: 'filter[quantity]'
          description: 'Quantity range filter'
          example: string
          required: false
          schema:
            type: string
            description: 'Quantity range filter'
            example: string
        -
          in: query
          name: 'filter[invoiced]'
          description: 'Invoiced scope'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Invoiced scope'
            example: true
        -
          in: query
          name: sort
          description: 'Field (`id`, `created_at`, `updated_at`, `quantity`, `buying_price_without_tax`, `selling_price_without_tax`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Field (`id`, `created_at`, `updated_at`, `quantity`, `buying_price_without_tax`, `selling_price_without_tax`)'
            example: string
        -
          in: query
          name: include
          description: 'Comma-separated relations (`invoice`, `file`, `item`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Comma-separated relations (`invoice`, `file`, `item`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 20)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 20)'
            example: 1
      responses:
        200:
          description: 'Paginated list of initial items.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    post:
      summary: 'Create a new initial line item.'
      operationId: createANewInitialLineItem
      description: 'Create a new initial line item.'
      parameters: []
      responses:
        201:
          description: 'Created item.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation/DB error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: 'Server error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
  '/api/v1/customer-files/initials/{id}':
    get:
      summary: 'Get a single initial item with relations (`file`, `invoice`, `item`).'
      operationId: getASingleInitialItemWithRelationsfileinvoiceitem
      description: 'Get a single initial item with relations (`file`, `invoice`, `item`).'
      parameters: []
      responses:
        200:
          description: 'Initial item object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    put:
      summary: 'Update an initial line item.'
      operationId: updateAnInitialLineItem
      description: 'Update an initial line item.'
      parameters: []
      responses:
        201:
          description: 'Updated item.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    delete:
      summary: 'Delete an initial line item.'
      operationId: deleteAnInitialLineItem
      description: 'Delete an initial line item.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/customer-files/recurrings:
    get:
      summary: 'List recurring items with filtering, sorting, and pagination.'
      operationId: listRecurringItemsWithFilteringSortingAndPagination
      description: 'List recurring items with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[label]'
          description: 'Filter by label'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by label'
            example: string
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by ID'
            example: 1
        -
          in: query
          name: 'filter[customer_file_id]'
          description: 'Filter by customer file'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer file'
            example: 1
        -
          in: query
          name: 'filter[item_id]'
          description: 'Filter by catalog item'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by catalog item'
            example: 1
        -
          in: query
          name: 'filter[line_key]'
          description: 'Filter by line key'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by line key'
            example: string
        -
          in: query
          name: 'filter[paused]'
          description: 'Filter paused items'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter paused items'
            example: true
        -
          in: query
          name: 'filter[active]'
          description: 'Filter active items'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter active items'
            example: true
        -
          in: query
          name: 'filter[delivered]'
          description: 'Filter delivered items'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter delivered items'
            example: true
        -
          in: query
          name: 'filter[billing_frequency]'
          description: 'Filter by billing frequency'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by billing frequency'
            example: string
        -
          in: query
          name: 'filter[quantity]'
          description: 'Quantity range filter'
          example: string
          required: false
          schema:
            type: string
            description: 'Quantity range filter'
            example: string
        -
          in: query
          name: sort
          description: 'Field (`id`, `created_at`, `updated_at`, `quantity`, `buying_price_without_tax`, `selling_price_without_tax`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Field (`id`, `created_at`, `updated_at`, `quantity`, `buying_price_without_tax`, `selling_price_without_tax`)'
            example: string
        -
          in: query
          name: include
          description: 'Relations (`file`, `item`, `sales`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Relations (`file`, `item`, `sales`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 20)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 20)'
            example: 1
      responses:
        200:
          description: 'Paginated list of recurring items (includes sales count).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    post:
      summary: 'Create a new recurring line item.'
      operationId: createANewRecurringLineItem
      description: 'Create a new recurring line item.'
      parameters: []
      responses:
        201:
          description: 'Created item.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
  '/api/v1/customer-files/recurrings/{id}':
    get:
      summary: 'Get a single recurring item with relations (`file`, `item`, `sales`).'
      operationId: getASingleRecurringItemWithRelationsfileitemsales
      description: 'Get a single recurring item with relations (`file`, `item`, `sales`).'
      parameters: []
      responses:
        200:
          description: 'Recurring item object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    put:
      summary: 'Update a recurring line item.'
      operationId: updateARecurringLineItem
      description: 'Update a recurring line item.'
      parameters: []
      responses:
        201:
          description: 'Updated item.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    delete:
      summary: 'Delete a recurring line item.'
      operationId: deleteARecurringLineItem
      description: 'Delete a recurring line item.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/customer-files/licences:
    get:
      summary: 'List licence items with filtering, sorting, and pagination.'
      operationId: listLicenceItemsWithFilteringSortingAndPagination
      description: 'List licence items with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[label]'
          description: 'Filter by label'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by label'
            example: string
        -
          in: query
          name: 'filter[item.description]'
          description: 'Filter by item description'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by item description'
            example: string
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by ID'
            example: 1
        -
          in: query
          name: 'filter[item.id]'
          description: 'Filter by item ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by item ID'
            example: 1
        -
          in: query
          name: 'filter[customer_file_id]'
          description: 'Filter by customer file'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer file'
            example: 1
        -
          in: query
          name: 'filter[item_id]'
          description: 'Filter by catalog item'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by catalog item'
            example: 1
        -
          in: query
          name: 'filter[line_key]'
          description: 'Filter by line key'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by line key'
            example: string
        -
          in: query
          name: 'filter[active]'
          description: 'Filter active licences'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter active licences'
            example: true
        -
          in: query
          name: 'filter[frequency]'
          description: 'Filter by frequency'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by frequency'
            example: string
        -
          in: query
          name: 'filter[quantity]'
          description: 'Quantity range filter'
          example: string
          required: false
          schema:
            type: string
            description: 'Quantity range filter'
            example: string
        -
          in: query
          name: sort
          description: 'Field (`id`, `created_at`, `updated_at`, `quantity`, `buying_price_without_tax`, `selling_price_without_tax`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Field (`id`, `created_at`, `updated_at`, `quantity`, `buying_price_without_tax`, `selling_price_without_tax`)'
            example: string
        -
          in: query
          name: include
          description: 'Relations (`invoice`, `file`, `item`, `sales`, `renewals`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Relations (`invoice`, `file`, `item`, `sales`, `renewals`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 20)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 20)'
            example: 1
      responses:
        200:
          description: 'Paginated list of licence items.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    post:
      summary: 'Create a new licence line item.'
      operationId: createANewLicenceLineItem
      description: 'Create a new licence line item.'
      parameters: []
      responses:
        201:
          description: 'Created item.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
  '/api/v1/customer-files/licences/{id}':
    get:
      summary: 'Get a single licence item with relations (`file`, `item`, `sales`).'
      operationId: getASingleLicenceItemWithRelationsfileitemsales
      description: 'Get a single licence item with relations (`file`, `item`, `sales`).'
      parameters: []
      responses:
        200:
          description: 'Licence item object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    put:
      summary: 'Update a licence line item.'
      operationId: updateALicenceLineItem
      description: 'Update a licence line item.'
      parameters: []
      responses:
        201:
          description: 'Updated item.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    delete:
      summary: 'Delete a licence line item.'
      operationId: deleteALicenceLineItem
      description: 'Delete a licence line item.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Files'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/customer-services:
    get:
      summary: 'List all customer services with filtering, sorting, and pagination.'
      operationId: listAllCustomerServicesWithFilteringSortingAndPagination
      description: 'List all customer services with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter by name'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by name'
            example: string
        -
          in: query
          name: 'filter[key]'
          description: 'Filter by key'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by key'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field (`id`, `name`, `key`, `created_at`, `updated_at`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field (`id`, `name`, `key`, `created_at`, `updated_at`)'
            example: string
      responses:
        200:
          description: 'Paginated list (10 per page) of customer service objects with the count of associated customers.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Services'
  '/api/v1/customer-services/{id}':
    get:
      summary: 'Get a single customer service by ID.'
      operationId: getASingleCustomerServiceByID
      description: 'Get a single customer service by ID.'
      parameters: []
      responses:
        200:
          description: 'Customer service object with associated customers (id, name, customer_account only).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Services'
    parameters:
      -
        in: path
        name: id
        description: 'Customer service ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/customers/{customer}/services':
    get:
      summary: 'List services assigned to a customer.'
      operationId: listServicesAssignedToACustomer
      description: 'List services assigned to a customer.'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter by name'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by name'
            example: string
        -
          in: query
          name: 'filter[key]'
          description: 'Filter by key'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by key'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field (`id`, `name`, `key`, `created_at`, `updated_at`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field (`id`, `name`, `key`, `created_at`, `updated_at`)'
            example: string
      responses:
        200:
          description: 'Paginated list (10 per page) of services assigned to the customer.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Services'
    post:
      summary: 'Sync the full set of services for a customer.'
      operationId: syncTheFullSetOfServicesForACustomer
      description: 'Sync the full set of services for a customer. This replaces all existing service assignments.'
      parameters: []
      responses:
        201:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Customer Services'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                services:
                  type: array
                  description: 'Array of service IDs to assign'
                  example:
                    - string
                  items:
                    type: string
              required:
                - services
    parameters:
      -
        in: path
        name: customer
        description: 'Customer ID (route model binding)'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/customers:
    get:
      summary: 'List all customers with pagination and sorting.'
      operationId: listAllCustomersWithPaginationAndSorting
      description: "List all customers with pagination and sorting. Results are filtered based on the authenticated user's role."
      parameters:
        -
          in: query
          name: nb
          description: 'Items per page (default: varies)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default: varies)'
            example: 1
        -
          in: query
          name: sort
          description: 'Field to sort by'
          example: string
          required: false
          schema:
            type: string
            description: 'Field to sort by'
            example: string
        -
          in: query
          name: direction
          description: 'Sort direction (`asc` / `desc`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort direction (`asc` / `desc`)'
            example: string
      responses:
        200:
          description: 'Paginated list of customer objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
    post:
      summary: 'Create a new customer.'
      operationId: createANewCustomer
      description: 'Create a new customer. A `customer_account` is auto-generated (format: `CL` + zero-padded ID).'
      parameters: []
      responses:
        201:
          description: 'Created customer object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
  '/api/v1/customers/{id}':
    get:
      summary: 'Get a single customer with related contacts.'
      operationId: getASingleCustomerWithRelatedContacts
      description: 'Get a single customer with related contacts.'
      parameters: []
      responses:
        200:
          description: 'Customer object with `contacts` relation.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
    put:
      summary: 'Update a customer.'
      operationId: updateACustomer
      description: 'Update a customer. Resets the `quick_search_index` flag.'
      parameters: []
      responses:
        200:
          description: 'Updated customer object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
    delete:
      summary: 'Delete a customer.'
      operationId: deleteACustomer
      description: 'Delete a customer. Fails if the customer has invoices or customer files. Requires admin/tech role.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        403:
          description: 'Forbidden (insufficient role or ownership).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
    parameters:
      -
        in: path
        name: id
        description: 'Customer ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/customers/search:
    get:
      summary: 'Search customers by text query.'
      operationId: searchCustomersByTextQuery
      description: 'Search customers by text query. Returns up to 15 results.'
      parameters:
        -
          in: query
          name: term
          description: 'Search term'
          example: string
          required: true
          schema:
            type: string
            description: 'Search term'
            example: string
      responses:
        200:
          description: 'Array of matching customer objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
  /api/v1/customers/lookup:
    get:
      summary: 'Look up a customer by account number or phone number.'
      operationId: lookUpACustomerByAccountNumberOrPhoneNumber
      description: 'Look up a customer by account number or phone number. Used by call center integrations.'
      parameters:
        -
          in: query
          name: customer_account
          description: 'Customer account number (e.g. `CL000123`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Customer account number (e.g. `CL000123`)'
            example: string
        -
          in: query
          name: phone_number
          description: 'Phone number'
          example: string
          required: false
          schema:
            type: string
            description: 'Phone number'
            example: string
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  found: true
                  data:
                    id: 1
                    name: ...
                    customer_account: CL000123
                    vip: false
                    on_call_duty: false
                    setup_follow_up: false
                    setup_follow_up_user: null
                properties:
                  found:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        example: 1
                      name:
                        type: string
                        example: ...
                      customer_account:
                        type: string
                        example: CL000123
                      vip:
                        type: boolean
                        example: false
                      on_call_duty:
                        type: boolean
                        example: false
                      setup_follow_up:
                        type: boolean
                        example: false
                      setup_follow_up_user:
                        type: string
                        example: null
                        nullable: true
      tags:
        - Customers
  '/api/v1/customers/{id}/consumptions':
    get:
      summary: 'Get total consumption amount for a customer in a given billing month.'
      operationId: getTotalConsumptionAmountForACustomerInAGivenBillingMonth
      description: 'Get total consumption amount for a customer in a given billing month.'
      parameters:
        -
          in: query
          name: month
          description: 'Month in `YYYYMM` format'
          example: string
          required: true
          schema:
            type: string
            description: 'Month in `YYYYMM` format'
            example: string
      responses:
        200:
          description: 'Total consumption amount (numeric).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
    parameters:
      -
        in: path
        name: id
        description: 'Customer ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/customers/customer_accounts/{customer_account}':
    get:
      summary: 'Get the internal customer ID from a customer account string.'
      operationId: getTheInternalCustomerIDFromACustomerAccountString
      description: 'Get the internal customer ID from a customer account string.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  customer_id: 42
                properties:
                  customer_id:
                    type: integer
                    example: 42
      tags:
        - Customers
    parameters:
      -
        in: path
        name: customer_account
        description: 'Account number (e.g. `CL000123`)'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/customer_accounts/{customer_account}':
    get:
      summary: 'Get the full customer object from a customer account string.'
      operationId: getTheFullCustomerObjectFromACustomerAccountString
      description: 'Get the full customer object from a customer account string.'
      parameters: []
      responses:
        200:
          description: 'Customer object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Customers
    parameters:
      -
        in: path
        name: customer_account
        description: 'Account number (e.g. `CL000123`)'
        example: string
        required: true
        schema:
          type: string
  /api/v1/documents:
    get:
      summary: 'List all documents for a specific entity.'
      operationId: listAllDocumentsForASpecificEntity
      description: 'List all documents for a specific entity.'
      parameters:
        -
          in: query
          name: documentable_type
          description: 'PHP class name of the entity (e.g. `App\Customer`)'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the entity (e.g. `App\Customer`)'
            example: string
        -
          in: query
          name: documentable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        200:
          description: 'Array of document metadata objects (`document_resource` collection).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Documents
    post:
      summary: 'Upload a new document and attach it to an entity.'
      operationId: uploadANewDocumentAndAttachItToAnEntity
      description: 'Upload a new document and attach it to an entity. The file is stored on all configured disks (Swift storage).'
      parameters:
        -
          in: query
          name: documentable_type
          description: 'PHP class name of the entity'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the entity'
            example: string
        -
          in: query
          name: documentable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        201:
          description: 'Created document metadata object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Documents
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: 'File to upload'
                file_description:
                  type: string
                  description: 'Description of the document'
                  example: string
                tags:
                  type: array
                  description: 'Tags for categorization'
                  example:
                    - string
                  items:
                    type: string
                shared:
                  type: boolean
                  description: 'Whether document is shared'
                  example: true
                user_id:
                  type: integer
                  description: 'Uploader user ID (defaults to authenticated user)'
                  example: 1
              required:
                - file
  '/api/v1/documents/{id}':
    get:
      summary: "Get a single document's metadata by ID."
      operationId: getASingleDocumentsMetadataByID
      description: "Get a single document's metadata by ID."
      parameters: []
      responses:
        200:
          description: 'Document metadata (`document_resource`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Documents
    put:
      summary: 'Update document metadata (description, shared status, tags).'
      operationId: updateDocumentMetadatadescriptionSharedStatusTags
      description: 'Update document metadata (description, shared status, tags). The file itself cannot be replaced.'
      parameters: []
      responses:
        201:
          description: 'Updated document metadata.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Documents
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                file_description:
                  type: string
                  description: 'Updated description'
                  example: string
                shared:
                  type: boolean
                  description: 'Updated shared status'
                  example: true
                tags:
                  type: array
                  description: 'Updated tags'
                  example:
                    - string
                  items:
                    type: string
    delete:
      summary: 'Delete a document record and remove the file from all storage disks.'
      operationId: deleteADocumentRecordAndRemoveTheFileFromAllStorageDisks
      description: 'Delete a document record and remove the file from all storage disks.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Documents
    parameters:
      -
        in: path
        name: id
        description: 'Document ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/documents/{id}/content':
    get:
      summary: 'Download the actual document file from Swift storage.'
      operationId: downloadTheActualDocumentFileFromSwiftStorage
      description: 'Download the actual document file from Swift storage.'
      parameters: []
      responses:
        200:
          description: 'File content with correct `Content-Type` header.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'File not found on storage.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Documents
    parameters:
      -
        in: path
        name: id
        description: 'Document ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/establishments:
    get:
      summary: 'List all establishments for a given fileable entity.'
      operationId: listAllEstablishmentsForAGivenFileableEntity
      description: 'List all establishments for a given fileable entity.'
      parameters:
        -
          in: query
          name: fileable_type
          description: 'Short name of the owning entity: `customer` or `prospect`'
          example: string
          required: true
          schema:
            type: string
            description: 'Short name of the owning entity: `customer` or `prospect`'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        200:
          description: 'Array of establishment objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Establishments
    post:
      summary: 'Create a new establishment attached to a customer or prospect.'
      operationId: createANewEstablishmentAttachedToACustomerOrProspect
      description: 'Create a new establishment attached to a customer or prospect.'
      parameters:
        -
          in: query
          name: fileable_type
          description: 'Short name of the owning entity: `customer` or `prospect`'
          example: string
          required: true
          schema:
            type: string
            description: 'Short name of the owning entity: `customer` or `prospect`'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        201:
          description: 'Created establishment object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Establishments
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'Establishment name'
                  example: string
                registrationNumber:
                  type: string
                  description: 'SIRET or registration number'
                  example: string
                addressLine1:
                  type: string
                  description: 'Address line 1'
                  example: string
                addressLine2:
                  type: string
                  description: 'Address line 2'
                  example: string
                postalCode:
                  type: string
                  description: 'Postal / ZIP code'
                  example: string
                city:
                  type: string
                  description: City
                  example: string
                country:
                  type: string
                  description: 'Country (default: `France`)'
                  example: string
  '/api/v1/establishments/{id}':
    get:
      summary: 'Get a single establishment by ID.'
      operationId: getASingleEstablishmentByID
      description: 'Get a single establishment by ID.'
      parameters: []
      responses:
        200:
          description: 'Establishment object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Establishments
    put:
      summary: 'Update an establishment.'
      operationId: updateAnEstablishment
      description: 'Update an establishment. Only address fields can be changed; `fileable_type` and `fileable_id` cannot be modified after creation.'
      parameters: []
      responses:
        201:
          description: 'Updated establishment object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Establishments
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'Establishment name'
                  example: string
                registrationNumber:
                  type: string
                  description: 'SIRET or registration number'
                  example: string
                addressLine1:
                  type: string
                  description: 'Address line 1'
                  example: string
                addressLine2:
                  type: string
                  description: 'Address line 2'
                  example: string
                postalCode:
                  type: string
                  description: 'Postal / ZIP code'
                  example: string
                city:
                  type: string
                  description: City
                  example: string
                country:
                  type: string
                  description: Country
                  example: string
    delete:
      summary: 'Delete an establishment.'
      operationId: deleteAnEstablishment
      description: 'Delete an establishment. Associated customer files that reference this establishment will have their `establishment_id` automatically set to `null` before deletion.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Establishments
    parameters:
      -
        in: path
        name: id
        description: 'Establishment ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/hosts:
    get:
      summary: 'List hosts.'
      operationId: listHosts
      description: 'List hosts. Supports filtering by customer.'
      parameters:
        -
          in: query
          name: 'filter[customer_id]'
          description: 'Filter by customer'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer'
            example: 1
      responses:
        200:
          description: 'Array of host objects with relations: `customer`, `os`, `role`, `shell`, `type`, `infrastructure`.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
    post:
      summary: 'Create a new host.'
      operationId: createANewHost
      description: 'Create a new host.'
      parameters: []
      responses:
        201:
          description: 'Created host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
  '/api/v1/hosts/{id}':
    get:
      summary: 'Get a single host with all relations: `customer`, `datacenter`, `documents`, `softwares`, `interfaces`, `os`, `role`, `s'
      operationId: getASingleHostWithAllRelationscustomerdatacenterdocumentssoftwaresinterfacesosroles
      description: 'Get a single host with all relations: `customer`, `datacenter`, `documents`, `softwares`, `interfaces`, `os`, `role`, `shell`, `supplier`, `type`, `sheets`, `tickets`.'
      parameters: []
      responses:
        200:
          description: 'Host object with full relations.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
    put:
      summary: 'Update a host.'
      operationId: updateAHost
      description: 'Update a host. Date/datetime fields are automatically converted.'
      parameters: []
      responses:
        201:
          description: 'Updated host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
    delete:
      summary: 'Delete a host.'
      operationId: deleteAHost
      description: 'Delete a host.'
      parameters: []
      responses:
        201:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/hosts/{host}/soft_attach':
    post:
      summary: 'Attach software(s) to a host (many-to-many, without detaching existing).'
      operationId: attachSoftwaresToAHostmanyToManyWithoutDetachingExisting
      description: 'Attach software(s) to a host (many-to-many, without detaching existing).'
      parameters: []
      responses:
        201:
          description: 'Updated host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                host_softwares:
                  type: array
                  description: 'Array of `host_software` IDs to attach'
                  example:
                    - string
                  items:
                    type: string
    parameters:
      -
        in: path
        name: host
        description: 'Host ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/hosts/{host}/soft_detach':
    post:
      summary: 'Detach specific software(s) from a host.'
      operationId: detachSpecificSoftwaresFromAHost
      description: 'Detach specific software(s) from a host.'
      parameters: []
      responses:
        201:
          description: 'Updated host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Hosts and Infrastructure'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                host_softwares:
                  type: array
                  description: 'Array of `host_software` IDs to detach'
                  example:
                    - string
                  items:
                    type: string
    parameters:
      -
        in: path
        name: host
        description: 'Host ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/incidents:
    get:
      summary: 'List all public, unresolved incidents.'
      operationId: listAllPublicUnresolvedIncidents
      description: 'List all public, unresolved incidents.'
      parameters: []
      responses:
        200:
          description: 'Array of incident objects with `scope`, `severity`, and `state` relations.'
          content:
            text/plain:
              schema:
                type: string
                example: "[\n  {\n    \"id\": 1,\n    \"title\": \"...\",\n    \"description\": \"...\",\n    \"started_at\": \"2024-01-01T10:00:00Z\",\n    \"resolved_at\": null,\n    \"is_public\": true,\n    \"scope\": { ... },\n    \"severity\": { ... },\n    \"state\": { ... }\n  }\n]"
      tags:
        - Incidents
  '/api/v1/incidents/{id}':
    get:
      summary: 'Get a single public incident by ID, including its updates.'
      operationId: getASinglePublicIncidentByIDIncludingItsUpdates
      description: 'Get a single public incident by ID, including its updates.'
      parameters: []
      responses:
        200:
          description: 'Incident object with `scope`, `severity`, `state`, and `updates` relations.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found or not public.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Incidents
    parameters:
      -
        in: path
        name: id
        description: 'Incident ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/incoming_mails:
    get:
      summary: 'List incoming mails, with optional filtering.'
      operationId: listIncomingMailsWithOptionalFiltering
      description: 'List incoming mails, with optional filtering.'
      parameters:
        -
          in: query
          name: filter
          description: 'Optional filter: `out_ticket` (mails without a ticket), `reply_to_closed_ticket` (replies to closed tickets)'
          example: string
          required: false
          schema:
            type: string
            description: 'Optional filter: `out_ticket` (mails without a ticket), `reply_to_closed_ticket` (replies to closed tickets)'
            example: string
      responses:
        200:
          description: 'Paginated list of incoming mail objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Incoming Mails'
  '/api/v1/incoming_mails/{incoming_mail}':
    delete:
      summary: 'Delete an incoming mail.'
      operationId: deleteAnIncomingMail
      description: 'Delete an incoming mail.'
      parameters: []
      responses:
        201:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Incoming Mails'
    parameters:
      -
        in: path
        name: incoming_mail
        description: 'Incoming mail ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/incoming_mails/{incoming_mail}/ticket':
    put:
      summary: 'Convert an incoming mail into a new ticket.'
      operationId: convertAnIncomingMailIntoANewTicket
      description: 'Convert an incoming mail into a new ticket.'
      parameters: []
      responses:
        201:
          description: 'Created ticket object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Mail or customer not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Incoming Mails'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer_id:
                  type: integer
                  description: 'Customer to associate the ticket with'
                  example: 1
              required:
                - customer_id
    parameters:
      -
        in: path
        name: incoming_mail
        description: 'Incoming mail ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/incoming_mails/{incoming_mail}/reply':
    put:
      summary: "Add an incoming mail as a reply to an existing ticket (determined by the mail's `ticket_id`)."
      operationId: addAnIncomingMailAsAReplyToAnExistingTicketdeterminedByTheMailsticketId
      description: "Add an incoming mail as a reply to an existing ticket (determined by the mail's `ticket_id`)."
      parameters: []
      responses:
        201:
          description: 'Created reply (ticket) object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Mail or ticket not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Incoming Mails'
    parameters:
      -
        in: path
        name: incoming_mail
        description: 'Incoming mail ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/mail-report:
    post:
      summary: 'Receive a mail delivery report from external mail systems.'
      operationId: receiveAMailDeliveryReportFromExternalMailSystems
      description: 'Receive a mail delivery report from external mail systems.'
      parameters: []
      responses: {  }
      tags:
        - 'Incoming Mails'
      security: []
  /api/v1/information-collections:
    get:
      summary: 'List information collections, optionally filtered by status or customer file.'
      operationId: listInformationCollectionsOptionallyFilteredByStatusOrCustomerFile
      description: 'List information collections, optionally filtered by status or customer file.'
      parameters:
        -
          in: query
          name: status
          description: 'Filter by status (e.g. `open`, `closed`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by status (e.g. `open`, `closed`)'
            example: string
        -
          in: query
          name: customer_file_id
          description: 'Filter by customer file'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer file'
            example: 1
      responses:
        200:
          description: 'Array of information collection objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    post:
      summary: 'Create a new information collection for a customer file.'
      operationId: createANewInformationCollectionForACustomerFile
      description: 'Create a new information collection for a customer file. Only creates one if none already exists for that file.'
      parameters:
        -
          in: query
          name: customer_file_id
          description: 'Customer file ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer file ID'
            example: 1
      responses:
        200:
          description: 'Created information collection object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error or already exists.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
  '/api/v1/information-collections/{id}':
    get:
      summary: 'Get a single information collection by ID.'
      operationId: getASingleInformationCollectionByID
      description: 'Get a single information collection by ID. Includes `customer_file` and `fileable` relations. Appends `front_url`.'
      parameters: []
      responses:
        200:
          description: 'Information collection object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    put:
      summary: 'Update an information collection.'
      operationId: updateAnInformationCollection
      description: 'Update an information collection.'
      parameters: []
      responses:
        201:
          description: 'Updated object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    delete:
      summary: 'Delete an information collection.'
      operationId: deleteAnInformationCollection
      description: 'Delete an information collection.'
      parameters: []
      responses:
        200:
          description: '`{ "message": "element deleted" }`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    parameters:
      -
        in: path
        name: id
        description: 'Information collection ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/information-collections/{id}/pbx-config':
    get:
      summary: 'Get the PBX configuration derived from an information collection.'
      operationId: getThePBXConfigurationDerivedFromAnInformationCollection
      description: 'Get the PBX configuration derived from an information collection. Returns the structured PBX config object.'
      parameters: []
      responses:
        200:
          description: 'PBX configuration object (`pbx_configuration::fromInformationCollection()`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    parameters:
      -
        in: path
        name: id
        description: 'Information collection ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/information-collections/providers-3cx:
    get:
      summary: 'Get available 3CX provider templates for use in information collection forms.'
      operationId: getAvailable3CXProviderTemplatesForUseInInformationCollectionForms
      description: 'Get available 3CX provider templates for use in information collection forms.'
      parameters: []
      responses:
        200:
          description: 'Array of 3CX provider template objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
  '/api/v1/customer-information-collection/{token}':
    get:
      summary: 'Get an information collection by its public token.'
      operationId: getAnInformationCollectionByItsPublicToken
      description: 'Get an information collection by its public token.'
      parameters: []
      responses:
        200:
          description: 'Information collection data.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Token not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    put:
      summary: 'Update an information collection via public token (customer filling in the form).'
      operationId: updateAnInformationCollectionViaPublicTokencustomerFillingInTheForm
      description: 'Update an information collection via public token (customer filling in the form).'
      parameters: []
      responses:
        200:
          description: 'Updated information collection.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    parameters:
      -
        in: path
        name: token
        description: 'Public access token'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/customer-information-collection/{token}/validate':
    get:
      summary: 'Check if the information collection is valid and complete.'
      operationId: checkIfTheInformationCollectionIsValidAndComplete
      description: 'Check if the information collection is valid and complete.'
      parameters: []
      responses:
        200:
          description: 'Validation result.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    parameters:
      -
        in: path
        name: token
        description: 'token identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/customer-information-collection/{token}/close':
    get:
      summary: 'Close/submit an information collection (mark as completed by customer).'
      operationId: closesubmitAnInformationCollectionmarkAsCompletedByCustomer
      description: 'Close/submit an information collection (mark as completed by customer).'
      parameters: []
      responses:
        200:
          description: 'Closed collection.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    parameters:
      -
        in: path
        name: token
        description: 'token identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/customer-information-collection/{token}/file':
    post:
      summary: 'Upload a file attachment to an information collection via public token.'
      operationId: uploadAFileAttachmentToAnInformationCollectionViaPublicToken
      description: 'Upload a file attachment to an information collection via public token.'
      parameters: []
      responses:
        201:
          description: 'Stored file.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Information Collections'
    parameters:
      -
        in: path
        name: token
        description: 'token identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/invoices:
    get:
      summary: 'List invoices for a customer.'
      operationId: listInvoicesForACustomer
      description: 'List invoices for a customer.'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Filter by customer ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Filter by customer ID'
            example: 1
      responses:
        200:
          description: 'Array of invoice objects (`invoice_light_resource` collection).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
  '/api/v1/invoices/{id}':
    get:
      summary: 'Get a single invoice with its `sales` relation.'
      operationId: getASingleInvoiceWithItssalesRelation
      description: 'Get a single invoice with its `sales` relation.'
      parameters: []
      responses:
        200:
          description: 'Invoice object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    parameters:
      -
        in: path
        name: id
        description: 'Invoice ID'
        example: 1
        required: true
        schema:
          type: integer
  '/api/v1/invoices/{id}/content':
    get:
      summary: 'Get the invoice PDF document.'
      operationId: getTheInvoicePDFDocument
      description: 'Get the invoice PDF document.'
      parameters: []
      responses:
        200:
          description: 'PDF file (`Content-Type: application/pdf`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'PDF not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    parameters:
      -
        in: path
        name: id
        description: 'Invoice ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/invoices-preprod:
    get:
      summary: 'List preprod invoices.'
      operationId: listPreprodInvoices
      description: 'List preprod invoices. (Same interface as invoices.)'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Filter by customer ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Filter by customer ID'
            example: 1
      responses:
        200:
          description: 'Array of preprod invoice objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
  '/api/v1/invoices-preprod/{id}':
    get:
      summary: 'Get a single preprod invoice.'
      operationId: getASinglePreprodInvoice
      description: 'Get a single preprod invoice.'
      parameters: []
      responses:
        200:
          description: 'Preprod invoice object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/invoices-preprod/{id}/content':
    get:
      summary: 'Get the preprod invoice as a PDF (dynamically generated).'
      operationId: getThePreprodInvoiceAsAPDFdynamicallyGenerated
      description: 'Get the preprod invoice as a PDF (dynamically generated).'
      parameters: []
      responses:
        200:
          description: 'PDF file (`Content-Type: application/pdf`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/invoices-proforma:
    get:
      summary: 'List proforma invoices for a customer.'
      operationId: listProformaInvoicesForACustomer
      description: 'List proforma invoices for a customer.'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Filter by customer ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Filter by customer ID'
            example: 1
      responses:
        200:
          description: 'Array of proforma invoice objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    post:
      summary: 'Create a new proforma invoice.'
      operationId: createANewProformaInvoice
      description: 'Create a new proforma invoice. Defaults: `total_without_tax: 0`, `tax: 0`, `total_with_tax: 0`, `credit: 0`, `net_to_pay: 0`, `automatic: false`, `date: now`.'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Customer to create proforma for'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer to create proforma for'
            example: 1
      responses:
        201:
          description: 'Created proforma invoice object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
  '/api/v1/invoices-proforma/{id}':
    get:
      summary: 'Get a single proforma invoice with its `sales` relation.'
      operationId: getASingleProformaInvoiceWithItssalesRelation
      description: 'Get a single proforma invoice with its `sales` relation.'
      parameters: []
      responses:
        200:
          description: 'Proforma invoice object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    delete:
      summary: 'Delete a proforma invoice.'
      operationId: deleteAProformaInvoice
      description: 'Delete a proforma invoice.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/invoices-proforma/{id}/content':
    get:
      summary: 'Get the proforma invoice as a PDF (dynamically generated).'
      operationId: getTheProformaInvoiceAsAPDFdynamicallyGenerated
      description: 'Get the proforma invoice as a PDF (dynamically generated).'
      parameters: []
      responses:
        200:
          description: 'PDF file (`Content-Type: application/pdf`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/invoices-proforma/{id}/publish':
    post:
      summary: 'Publish a proforma invoice: converts it to a real invoice via the `publish_proforma_invoice` action.'
      operationId: publishAProformaInvoiceConvertsItToARealInvoiceViaThepublishProformaInvoiceAction
      description: 'Publish a proforma invoice: converts it to a real invoice via the `publish_proforma_invoice` action.'
      parameters: []
      responses:
        201:
          description: 'Published invoice object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Invoices
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                payment_term:
                  type: string
                  description: 'Payment term'
                  example: string
                credit:
                  type: string
                  description: 'Credit amount'
                  example: string
                paymentMethod_id:
                  type: integer
                  description: 'Payment method ID'
                  example: 1
                deposit_method:
                  type: string
                  description: 'Deposit method'
                  example: string
    parameters:
      -
        in: path
        name: id
        description: 'Proforma invoice ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/notes:
    get:
      summary: 'List all notes for a specific entity.'
      operationId: listAllNotesForASpecificEntity
      description: 'List all notes for a specific entity.'
      parameters:
        -
          in: query
          name: noteable_type
          description: 'PHP class name of the entity (e.g. `App\Customer`)'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the entity (e.g. `App\Customer`)'
            example: string
        -
          in: query
          name: noteable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        200:
          description: 'Array of note objects (`note_resource` collection).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Notes
    post:
      summary: 'Create a new note for an entity.'
      operationId: createANewNoteForAnEntity
      description: 'Create a new note for an entity.'
      parameters:
        -
          in: query
          name: noteable_type
          description: 'PHP class name of the entity'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the entity'
            example: string
        -
          in: query
          name: noteable_id
          description: 'ID of the owning entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the owning entity'
            example: 1
      responses:
        201:
          description: 'Created note object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Notes
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                  description: 'Note content'
                  example: string
              required:
                - body
  '/api/v1/notes/{id}':
    get:
      summary: 'Get a single note by ID.'
      operationId: getASingleNoteByID
      description: 'Get a single note by ID.'
      parameters: []
      responses:
        200:
          description: 'Note object (`note_resource`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Notes
    put:
      summary: 'Update a note (body only; `id`, `created_at`, `updated_at` are excluded).'
      operationId: updateANotebodyOnlyidcreatedAtupdatedAtAreExcluded
      description: 'Update a note (body only; `id`, `created_at`, `updated_at` are excluded).'
      parameters: []
      responses:
        201:
          description: 'Updated note object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Notes
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                  description: 'Updated note content'
                  example: string
              required:
                - body
    delete:
      summary: 'Delete a note.'
      operationId: deleteANote
      description: 'Delete a note.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Notes
    parameters:
      -
        in: path
        name: id
        description: 'Note ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/organisations:
    get:
      summary: 'List all organisations.'
      operationId: listAllOrganisations
      description: 'List all organisations.'
      parameters: []
      responses:
        200:
          description: 'Array of organisation objects (`organisation_resource` collection, all records, no pagination).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Organisations and Brands'
  '/api/v1/organisations/{id}':
    get:
      summary: 'Get a single organisation by ID.'
      operationId: getASingleOrganisationByID
      description: 'Get a single organisation by ID.'
      parameters: []
      responses:
        200:
          description: 'Organisation object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Organisations and Brands'
    put:
      summary: 'Update an organisation.'
      operationId: updateAnOrganisation
      description: 'Update an organisation. Only fields listed in `Organisation::updatable()` are updated.'
      parameters: []
      responses:
        201:
          description: 'Updated organisation object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Organisations and Brands'
    parameters:
      -
        in: path
        name: id
        description: 'Organisation ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/brands:
    get:
      summary: 'List all brands.'
      operationId: listAllBrands
      description: 'List all brands.'
      parameters: []
      responses:
        200:
          description: 'Array of all brand objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Organisations and Brands'
  '/api/v1/brands/{id}':
    get:
      summary: 'Get a single brand by ID.'
      operationId: getASingleBrandByID
      description: 'Get a single brand by ID.'
      parameters: []
      responses:
        200:
          description: 'Brand object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Organisations and Brands'
    put:
      summary: 'Update a brand.'
      operationId: updateABrand
      description: 'Update a brand. Handles image uploads: - `logo`: resized to max 500px width, stored on Swift and FTP disks. - `message_on_invoice`: resized to 2126x709px, stored on Swift and FTP disks.'
      parameters: []
      responses:
        201:
          description: 'Updated brand object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Organisations and Brands'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                logo:
                  type: string
                  description: 'Brand logo (optional)'
                  example: string
                message_on_invoice:
                  type: string
                  description: 'Invoice message image (optional)'
                  example: string
    parameters:
      -
        in: path
        name: id
        description: 'Brand ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/pbx3cx-hosts:
    get:
      summary: 'List all 3CX hosts.'
      operationId: listAll3CXHosts
      description: 'List all 3CX hosts.'
      parameters:
        -
          in: query
          name: customer_id
          description: 'Filter by customer'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer'
            example: 1
      responses:
        200:
          description: 'Array of 3CX host objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    post:
      summary: 'Create a new 3CX host.'
      operationId: createANew3CXHost
      description: 'Create a new 3CX host. A code is auto-generated.'
      parameters: []
      responses:
        201:
          description: 'Created host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
  /api/v1/pbx3cx-hosts/lookup:
    get:
      summary: 'Look up a 3CX host by its hostname.'
      operationId: lookUpA3CXHostByItsHostname
      description: 'Look up a 3CX host by its hostname. Returns the host record with its associated customers. Use this to resolve a customer from a PBX alert when the monitoring system only knows the FQDN.'
      parameters:
        -
          in: query
          name: host_name
          description: 'Hostname or FQDN of the 3CX instance (e.g. `acme.3cx.fr`)'
          example: string
          required: true
          schema:
            type: string
            description: 'Hostname or FQDN of the 3CX instance (e.g. `acme.3cx.fr`)'
            example: string
      responses:
        200:
          description: 'Host object with `customers` relation.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Missing `host_name`.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Host not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
  /api/v1/pbx3cx-hosts/lookup-by-customer:
    get:
      summary: 'List all 3CX hosts registered for a customer, identified by their account number.'
      operationId: listAll3CXHostsRegisteredForACustomerIdentifiedByTheirAccountNumber
      description: 'List all 3CX hosts registered for a customer, identified by their account number.'
      parameters:
        -
          in: query
          name: customer_account
          description: 'Customer account code (e.g. `CL2385`)'
          example: string
          required: true
          schema:
            type: string
            description: 'Customer account code (e.g. `CL2385`)'
            example: string
      responses:
        200:
          description: '`{ "data": [ { host records } ] }`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Missing `customer_account`.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Customer not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
  '/api/v1/pbx3cx-hosts/{id}':
    get:
      summary: 'Get a single 3CX host by ID.'
      operationId: getASingle3CXHostByID
      description: 'Get a single 3CX host by ID.'
      parameters: []
      responses:
        200:
          description: 'Host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    put:
      summary: 'Update a 3CX host.'
      operationId: updateA3CXHost
      description: 'Update a 3CX host.'
      parameters: []
      responses:
        201:
          description: 'Updated host object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    delete:
      summary: 'Delete a 3CX host.'
      operationId: deleteA3CXHost
      description: 'Delete a 3CX host.'
      parameters: []
      responses:
        201:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/pbx3cx/{pbx3cx}/origins':
    get:
      summary: 'Get the available origins for a specific 3CX host.'
      operationId: getTheAvailableOriginsForASpecific3CXHost
      description: 'Get the available origins for a specific 3CX host. Used for routing configuration.'
      parameters: []
      responses:
        200:
          description: 'Array of origin objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    parameters:
      -
        in: path
        name: pbx3cx
        description: '3CX host ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/pbx3cx-call-logs:
    get:
      summary: 'List all visible call log entries.'
      operationId: listAllVisibleCallLogEntries
      description: 'List all visible call log entries.'
      parameters: []
      responses:
        200:
          description: 'Array of call log objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    post:
      summary: 'Create a new call log entry.'
      operationId: createANewCallLogEntry
      description: 'Create a new call log entry. Triggers a refresh of call log data.'
      parameters: []
      responses:
        201:
          description: 'Created call log object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
  '/api/v1/pbx3cx-call-logs/{id}':
    get:
      summary: 'Get a single call log entry by ID.'
      operationId: getASingleCallLogEntryByID
      description: 'Get a single call log entry by ID.'
      parameters: []
      responses:
        200:
          description: 'Call log object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/pbx3cx-call-log-details/{id}':
    get:
      summary: 'Get detailed call log information for a specific call log ID.'
      operationId: getDetailedCallLogInformationForASpecificCallLogID
      description: 'Get detailed call log information for a specific call log ID.'
      parameters: []
      responses:
        200:
          description: 'Call log detail object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
    parameters:
      -
        in: path
        name: id
        description: 'Call log ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/helpers/3cxCountries:
    get:
      summary: 'Get the list of countries supported by 3CX (for PBX configuration).'
      operationId: getTheListOfCountriesSupportedBy3CXforPBXConfiguration
      description: 'Get the list of countries supported by 3CX (for PBX configuration).'
      parameters: []
      responses:
        200:
          description: 'Array of country objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
      security: []
  /api/v1/helpers/3cxOpenDestinations:
    get:
      summary: 'Get the list of open/unrestricted destinations for 3CX routing.'
      operationId: getTheListOfOpenunrestrictedDestinationsFor3CXRouting
      description: 'Get the list of open/unrestricted destinations for 3CX routing.'
      parameters: []
      responses:
        200:
          description: 'Array of destination objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'PBX (3CX)'
      security: []
  /api/v1/fileable-payment-plan:
    get:
      summary: 'Get the payment plan for a fileable entity.'
      operationId: getThePaymentPlanForAFileableEntity
      description: 'Get the payment plan for a fileable entity.'
      parameters:
        -
          in: query
          name: fileable_type
          description: 'PHP class name of the fileable entity'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the fileable entity'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the fileable entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the fileable entity'
            example: 1
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
                example: "{\n  \"payment_plan\": { ...payment plan object... }\n}"
      tags:
        - 'Payment Plans'
    post:
      summary: 'Create or update the payment plan for a fileable entity.'
      operationId: createOrUpdateThePaymentPlanForAFileableEntity
      description: 'Create or update the payment plan for a fileable entity. Note: this endpoint uses `POST` for both creation and update (not `PUT`).'
      parameters:
        -
          in: query
          name: fileable_type
          description: 'PHP class name of the fileable entity'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the fileable entity'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the fileable entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the fileable entity'
            example: 1
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
                example: "{\n  \"payment_plan\": { ...updated payment plan object... }\n}"
        422:
          description: 'Validation error (e.g. missing `payment_method_id` on creation).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Payment Plans'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                payment_method_id:
                  type: integer
                  description: 'Payment method ID'
                  example: 1
                signature_date:
                  type: string
                  description: 'Date format `Y-m-d`'
                  example: string
  /api/v1/fileable-payment-plan/history:
    get:
      summary: 'Get the activity audit log (changelog) for a payment plan.'
      operationId: getTheActivityAuditLogchangelogForAPaymentPlan
      description: 'Get the activity audit log (changelog) for a payment plan.'
      parameters:
        -
          in: query
          name: fileable_type
          description: 'PHP class name of the fileable entity'
          example: string
          required: true
          schema:
            type: string
            description: 'PHP class name of the fileable entity'
            example: string
        -
          in: query
          name: fileable_id
          description: 'ID of the fileable entity'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the fileable entity'
            example: 1
      responses:
        200:
          description: ''
          content:
            text/plain:
              schema:
                type: string
                example: "{\n  \"history\": [\n    {\n      \"id\": 1,\n      \"event\": \"updated\",\n      \"created_at\": \"2024-01-01T12:00:00Z\",\n      \"causer\": { \"id\": 5, \"name\": \"...\" },\n      \"properties\": { ... }\n    }\n  ]\n}"
      tags:
        - 'Payment Plans'
  /api/v1/phone-calls:
    get:
      summary: 'List phone calls with filtering, sorting, and pagination.'
      operationId: listPhoneCallsWithFilteringSortingAndPagination
      description: 'List phone calls with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by ID'
            example: 1
        -
          in: query
          name: 'filter[user_id]'
          description: 'Filter by user'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by user'
            example: 1
        -
          in: query
          name: 'filter[contact_id]'
          description: 'Filter by contact'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by contact'
            example: 1
        -
          in: query
          name: 'filter[status]'
          description: 'Filter by status'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by status'
            example: string
        -
          in: query
          name: 'filter[direction]'
          description: 'Filter by direction (`inbound` / `outbound`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by direction (`inbound` / `outbound`)'
            example: string
        -
          in: query
          name: 'filter[answered]'
          description: 'Filter answered calls'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter answered calls'
            example: true
        -
          in: query
          name: 'filter[number_from]'
          description: 'Filter by origin number'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by origin number'
            example: string
        -
          in: query
          name: 'filter[number_to]'
          description: 'Filter by destination number'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by destination number'
            example: string
        -
          in: query
          name: 'filter[started_at]'
          description: 'Filter by start timestamp'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by start timestamp'
            example: string
        -
          in: query
          name: 'filter[ended_at]'
          description: 'Filter by end timestamp'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by end timestamp'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 10)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 10)'
            example: 1
      responses:
        200:
          description: 'Paginated list of phone call objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Phone Calls'
    post:
      summary: 'Log a new phone call.'
      operationId: logANewPhoneCall
      description: 'Log a new phone call. `user_id` is auto-assigned. Timestamps are parsed. Status and direction are converted to their enum values.'
      parameters: []
      responses:
        201:
          description: 'Created phone call object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Phone Calls'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                contact_id:
                  type: integer
                  description: 'Linked contact'
                  example: 1
                customer_id:
                  type: integer
                  description: 'Linked customer (auto-resolved from contact if not provided)'
                  example: 1
                ticket_id:
                  type: integer
                  description: 'Linked ticket'
                  example: 1
                status:
                  type: string
                  description: 'Call status (enum: `phone_call_status`)'
                  example: string
                direction:
                  type: string
                  description: '`inbound` or `outbound` (`phone_call_direction` enum)'
                  example: string
                answered:
                  type: boolean
                  description: 'Whether the call was answered'
                  example: true
                number_from:
                  type: string
                  description: 'Originating number'
                  example: string
                number_to:
                  type: string
                  description: 'Destination number'
                  example: string
                started_at:
                  type: string
                  description: 'Start datetime (ISO 8601)'
                  example: string
                ended_at:
                  type: string
                  description: 'End datetime (ISO 8601)'
                  example: string
  '/api/v1/phone-calls/{id}':
    get:
      summary: 'Get a single phone call by ID.'
      operationId: getASinglePhoneCallByID
      description: 'Get a single phone call by ID.'
      parameters: []
      responses:
        200:
          description: 'Phone call object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Phone Calls'
    put:
      summary: 'Update a phone call record.'
      operationId: updateAPhoneCallRecord
      description: 'Update a phone call record. Creates a ticket activity record if a ticket is linked and no activity exists yet.'
      parameters: []
      responses:
        201:
          description: 'Updated phone call object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Phone Calls'
    delete:
      summary: 'Delete a phone call record.'
      operationId: deleteAPhoneCallRecord
      description: 'Delete a phone call record.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Phone Calls'
    parameters:
      -
        in: path
        name: id
        description: 'Phone call ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/prospects:
    get:
      summary: 'List prospects with filtering, sorting, and pagination (Spatie QueryBuilder).'
      operationId: listProspectsWithFilteringSortingAndPaginationSpatieQueryBuilder
      description: 'List prospects with filtering, sorting, and pagination (Spatie QueryBuilder). Filtered by user role: role-5 users only see prospects visible to sales (`viewable_by_sale` scope).'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter by name'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by name'
            example: string
        -
          in: query
          name: 'filter[id]'
          description: 'Filter by exact ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by exact ID'
            example: 1
        -
          in: query
          name: 'filter[term_match]'
          description: 'Full-text term match'
          example: string
          required: false
          schema:
            type: string
            description: 'Full-text term match'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field (`id`, `created_at`, `updated_at`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field (`id`, `created_at`, `updated_at`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page'
            example: 1
      responses:
        200:
          description: 'Paginated list of prospect objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Prospects
    post:
      summary: 'Create a new prospect.'
      operationId: createANewProspect
      description: 'Create a new prospect. A `customer_account` is auto-generated (format: `PR` + zero-padded ID). Role-5 users automatically have their ID set as `representative_id`.'
      parameters: []
      responses:
        201:
          description: 'Created prospect object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Prospects
  '/api/v1/prospects/{id}':
    get:
      summary: 'Get a single prospect with `payment_plan`, `contacts`, and `documents` loaded.'
      operationId: getASingleProspectWithpaymentPlancontactsAnddocumentsLoaded
      description: 'Get a single prospect with `payment_plan`, `contacts`, and `documents` loaded. Authorization policy enforced.'
      parameters: []
      responses:
        200:
          description: 'Prospect object with relations.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        403:
          description: Forbidden.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Prospects
    put:
      summary: 'Update a prospect.'
      operationId: updateAProspect
      description: 'Update a prospect. Role-5 users cannot change `representative_id`. Auto-generates `customer_account` if empty.'
      parameters: []
      responses:
        201:
          description: 'Updated prospect object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        403:
          description: 'Forbidden (role-5 trying to change rep).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Prospects
    delete:
      summary: 'Delete a prospect.'
      operationId: deleteAProspect
      description: 'Delete a prospect.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Prospects
    parameters:
      -
        in: path
        name: id
        description: 'Prospect ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/quotes:
    get:
      summary: 'Check if a customer file has quote documents, and list available types.'
      operationId: checkIfACustomerFileHasQuoteDocumentsAndListAvailableTypes
      description: 'Check if a customer file has quote documents, and list available types.'
      parameters:
        -
          in: query
          name: customer_file_id
          description: 'Customer file ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer file ID'
            example: 1
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  has_quotes: true
                  types:
                    - quote
                    - proforma
                    - contract
                properties:
                  has_quotes:
                    type: boolean
                    example: true
                  types:
                    type: array
                    example:
                      - quote
                      - proforma
                      - contract
                    items:
                      type: string
      tags:
        - 'Quotes & Documents'
  /api/v1/quotes/generate:
    get:
      summary: 'Generate quote documents for a customer file.'
      operationId: generateQuoteDocumentsForACustomerFile
      description: 'Generate quote documents for a customer file. Runs the `generate_quote_documents` action.'
      parameters:
        -
          in: query
          name: customer_file_id
          description: 'Customer file ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer file ID'
            example: 1
      responses:
        201:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  types:
                    - quote
                    - proforma
                    - contract
                properties:
                  types:
                    type: array
                    example:
                      - quote
                      - proforma
                      - contract
                    items:
                      type: string
        422:
          description: 'Error during generation.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: 'Error during generation.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Quotes & Documents'
  /api/v1/quotes/show:
    get:
      summary: 'Show (download) a specific quote document as a PDF.'
      operationId: showdownloadASpecificQuoteDocumentAsAPDF
      description: 'Show (download) a specific quote document as a PDF.'
      parameters:
        -
          in: query
          name: customer_file_id
          description: 'Customer file ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer file ID'
            example: 1
        -
          in: query
          name: type
          description: 'Document type: `quote`, `proforma`, or `contract`'
          example: string
          required: true
          schema:
            type: string
            description: 'Document type: `quote`, `proforma`, or `contract`'
            example: string
      responses:
        200:
          description: 'PDF file (`Content-Type: application/pdf`).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Document not found (no quotes generated).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Quotes & Documents'
  /api/v1/quotes/send:
    post:
      summary: 'Send a quote document by email to a recipient.'
      operationId: sendAQuoteDocumentByEmailToARecipient
      description: 'Send a quote document by email to a recipient.'
      parameters: []
      responses:
        200:
          description: '`{ "message": "success" }`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Quotes not yet generated.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: 'Mail sending error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Quotes & Documents'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                customer_file_id:
                  type: integer
                  description: 'Customer file ID'
                  example: 1
                email:
                  type: string
                  description: 'Recipient email address'
                  example: string
                type:
                  type: string
                  description: 'Document type: `quote`, `proforma`, or `contract`'
                  example: string
              required:
                - customer_file_id
                - email
                - type
  /api/v1/quotes/docusign:
    post:
      summary: 'Send a contract for electronic signature via DocuSign.'
      operationId: sendAContractForElectronicSignatureViaDocuSign
      description: 'Send a contract for electronic signature via DocuSign. Requires the DocuSign module to be enabled.'
      parameters:
        -
          in: query
          name: customer_file_id
          description: 'Customer file ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer file ID'
            example: 1
        -
          in: query
          name: type
          description: 'Must be `contract`'
          example: string
          required: true
          schema:
            type: string
            description: 'Must be `contract`'
            example: string
      responses:
        200:
          description: 'DocuSign envelope data.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Module not enabled or validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        500:
          description: 'DocuSign API error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Quotes & Documents'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  description: 'Envelope title (max 255 chars)'
                  example: string
                signers:
                  type: array
                  description: 'Array of signers (at least 1). Each: `email` (required), `name` (optional)'
                  example:
                    - string
                  items:
                    type: string
                cc:
                  type: array
                  description: 'Array of CC recipients. Each: `email` (required), `name` (optional)'
                  example:
                    - string
                  items:
                    type: string
                draft:
                  type: boolean
                  description: 'If true, creates envelope as draft'
                  example: true
              required:
                - title
                - signers
  /api/v1/recruitment/candidates:
    get:
      summary: 'List candidates with pagination and sorting.'
      operationId: listCandidatesWithPaginationAndSorting
      description: 'List candidates with pagination and sorting.'
      parameters:
        -
          in: query
          name: nb
          description: 'Items per page'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page'
            example: 1
        -
          in: query
          name: sort
          description: 'Sort field'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field'
            example: string
        -
          in: query
          name: direction
          description: 'Sort direction (`asc` / `desc`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort direction (`asc` / `desc`)'
            example: string
      responses:
        200:
          description: 'Paginated list of candidate objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
    post:
      summary: 'Create a candidate.'
      operationId: createACandidate
      description: 'Create a candidate.'
      parameters: []
      responses:
        201:
          description: 'Created candidate object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
  /api/v1/recruitment/candidates/search:
    get:
      summary: 'Search candidates by quick-search term.'
      operationId: searchCandidatesByQuickSearchTerm
      description: 'Search candidates by quick-search term.'
      parameters:
        -
          in: query
          name: term
          description: 'Search term'
          example: string
          required: true
          schema:
            type: string
            description: 'Search term'
            example: string
      responses:
        200:
          description: 'Array of matching candidates.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
  '/api/v1/recruitment/candidates/{id}':
    get:
      summary: 'Show a candidate with specialities, languages, marital status, candidate details, and contracts.'
      operationId: showACandidateWithSpecialitiesLanguagesMaritalStatusCandidateDetailsAndContracts
      description: 'Show a candidate with specialities, languages, marital status, candidate details, and contracts.'
      parameters: []
      responses:
        200:
          description: 'Candidate object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
    put:
      summary: 'Update a candidate.'
      operationId: updateACandidate
      description: 'Update a candidate. Supports the same relation fields as create.'
      parameters: []
      responses:
        200:
          description: 'Updated candidate object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
    delete:
      summary: 'Delete a candidate.'
      operationId: deleteACandidate
      description: 'Delete a candidate.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/recruitment/candidate-queries:
    get:
      summary: 'Query candidates with advanced filters.'
      operationId: queryCandidatesWithAdvancedFilters
      description: 'Query candidates with advanced filters.'
      parameters:
        -
          in: query
          name: 'filter[speciality_id]'
          description: 'Filter by speciality'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by speciality'
            example: 1
        -
          in: query
          name: 'filter[available_in_country]'
          description: 'Available for in-country assignments'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Available for in-country assignments'
            example: true
        -
          in: query
          name: 'filter[available_abroad]'
          description: 'Available for international assignments'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Available for international assignments'
            example: true
        -
          in: query
          name: 'filter[available_housed]'
          description: 'Available to be housed on-site'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Available to be housed on-site'
            example: true
        -
          in: query
          name: 'filter[available_on]'
          description: 'Available on a specific date'
          example: string
          required: false
          schema:
            type: string
            description: 'Available on a specific date'
            example: string
        -
          in: query
          name: 'filter[recruitment_filter_id]'
          description: 'Filter by recruitment filter ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by recruitment filter ID'
            example: 1
      responses:
        200:
          description: 'Filtered array of candidate objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
  /api/v1/recruitment/interviews/statuses:
    get:
      summary: 'Get the list of available interview statuses.'
      operationId: getTheListOfAvailableInterviewStatuses
      description: 'Get the list of available interview statuses.'
      parameters: []
      responses:
        200:
          description: 'Array of status strings.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
  /api/v1/recruitment/interviews:
    get:
      summary: 'List interviews.'
      operationId: listInterviews
      description: 'List interviews.'
      parameters:
        -
          in: query
          name: recruitment_id
          description: 'Filter by recruitment'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by recruitment'
            example: 1
        -
          in: query
          name: candidate_id
          description: 'Filter by candidate'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by candidate'
            example: 1
        -
          in: query
          name: customer_id
          description: 'Filter by customer'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by customer'
            example: 1
      responses:
        200:
          description: 'Array of interview objects with related recruitment and customer data.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Recruitment
  /api/v1/sales-proforma:
    get:
      summary: 'List proforma sales for a specific proforma invoice.'
      operationId: listProformaSalesForASpecificProformaInvoice
      description: 'List proforma sales for a specific proforma invoice.'
      parameters:
        -
          in: query
          name: invoice_proforma_id
          description: 'Filter by proforma invoice ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Filter by proforma invoice ID'
            example: 1
      responses:
        200:
          description: 'Array of proforma sale objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Sales (Proforma Lines and Credits)'
    post:
      summary: 'Create a new proforma sale line.'
      operationId: createANewProformaSaleLine
      description: 'Create a new proforma sale line. Prices are auto-calculated: buying price, selling price, taxes, discounts. The item can be identified by `item_id` or `bar_code`.'
      parameters:
        -
          in: query
          name: invoice_proforma_id
          description: 'Proforma invoice to add the line to'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Proforma invoice to add the line to'
            example: 1
      responses:
        201:
          description: 'Created proforma sale object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Invalid item or validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Sales (Proforma Lines and Credits)'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                item_id:
                  type: integer
                  description: 'Catalog item ID (alternatively: `bar_code`)'
                  example: 1
                bar_code:
                  type: string
                  description: 'Item barcode (alternative to `item_id`)'
                  example: string
                quantity:
                  type: string
                  description: Quantity
                  example: string
                discount_rate:
                  type: string
                  description: 'Discount percentage'
                  example: string
  '/api/v1/sales-proforma/{id}':
    get:
      summary: 'Get a single proforma sale line.'
      operationId: getASingleProformaSaleLine
      description: 'Get a single proforma sale line.'
      parameters: []
      responses:
        200:
          description: 'Proforma sale object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Sales (Proforma Lines and Credits)'
    put:
      summary: 'Update a proforma sale line.'
      operationId: updateAProformaSaleLine
      description: 'Update a proforma sale line. All pricing fields are recalculated.'
      parameters: []
      responses:
        201:
          description: 'Updated proforma sale object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Sales (Proforma Lines and Credits)'
    delete:
      summary: 'Delete a proforma sale line.'
      operationId: deleteAProformaSaleLine
      description: 'Delete a proforma sale line.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Sales (Proforma Lines and Credits)'
    parameters:
      -
        in: path
        name: id
        description: 'Proforma sale ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/sales/credit:
    post:
      summary: 'Apply a credit to a sale.'
      operationId: applyACreditToASale
      description: 'Apply a credit to a sale. This endpoint does NOT require authentication (`auth:api` middleware is NOT applied).'
      parameters: []
      responses:
        200:
          description: 'Credit result.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Sales (Proforma Lines and Credits)'
  /api/v1/sales-tasks:
    get:
      summary: 'List tasks for the authenticated user that are active and upcoming.'
      operationId: listTasksForTheAuthenticatedUserThatAreActiveAndUpcoming
      description: 'List tasks for the authenticated user that are active and upcoming. Used by sales dashboards and CRM integrations.'
      parameters: []
      responses:
        200:
          description: 'Array of task objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tasks
  /api/v1/tickets:
    get:
      summary: 'List open tickets, ordered by ID descending.'
      operationId: listOpenTicketsOrderedByIDDescending
      description: 'List open tickets, ordered by ID descending.'
      parameters:
        -
          in: query
          name: ticketable_type
          description: 'Filter by ticketable type. If provided together with `ticketable_id`, returns all tickets for that entity regardless of open/closed state'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by ticketable type. If provided together with `ticketable_id`, returns all tickets for that entity regardless of open/closed state'
            example: string
        -
          in: query
          name: ticketable_id
          description: 'Filter by ticketable ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by ticketable ID'
            example: 1
      responses:
        200:
          description: 'Array of ticket objects with `ticketable`, `assignee` (id, name, email), `replies_count`, and `last_reply_seen_at`.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    post:
      summary: 'Create a new ticket.'
      operationId: createANewTicket
      description: 'Create a new ticket. The authenticated user is set as the author and opener. The ticket is created unassigned unless an `assignee_id` is provided.'
      parameters:
        -
          in: query
          name: ticketable_type
          description: 'Polymorphic type (currently only `App\Customer`)'
          example: string
          required: true
          schema:
            type: string
            description: 'Polymorphic type (currently only `App\Customer`)'
            example: string
        -
          in: query
          name: ticketable_id
          description: 'ID of the entity to attach the ticket to'
          example: 1
          required: true
          schema:
            type: integer
            description: 'ID of the entity to attach the ticket to'
            example: 1
      responses:
        201:
          description: 'Created ticket object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Bad request (missing mandatory fields or invalid ticketable type).'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Ticketable entity not found, or `assignee_id` does not reference an active user.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                subject:
                  type: string
                  description: 'Ticket subject'
                  example: string
                body:
                  type: string
                  description: 'Ticket body (HTML)'
                  example: string
                priority_id:
                  type: integer
                  description: 'Priority ID'
                  example: 1
                ticket_category_id:
                  type: integer
                  description: 'Category ID'
                  example: 1
                ticket_sub_category_id:
                  type: integer
                  description: 'Sub-category ID'
                  example: 1
                estimated_resolution_date:
                  type: string
                  description: 'Format: `Y-m-d H:i:s`. Defaults to now + 4 hours'
                  example: string
                tags:
                  type: array
                  description: 'Array of tag strings'
                  example:
                    - string
                  items:
                    type: string
                team_id:
                  type: integer
                  description: 'Support team ID'
                  example: 1
                status_id:
                  type: integer
                  description: 'Initial status ID'
                  example: 1
                assignee_id:
                  type: integer
                  description: "If provided, must reference an active user. The ticket is assigned to that user; otherwise it's created unassigned"
                  example: 1
                close:
                  type: boolean
                  description: 'If `true`, immediately close the ticket after creation'
                  example: true
              required:
                - subject
                - body
                - priority_id
                - ticket_category_id
                - ticket_sub_category_id
  '/api/v1/tickets/{id}':
    get:
      summary: 'Get a single ticket with all related data.'
      operationId: getASingleTicketWithAllRelatedData
      description: 'Get a single ticket with all related data.'
      parameters: []
      responses:
        200:
          description: 'Ticket object with `replies` (including `author`), `ticketable`, `contacts`, `category`, `sub_category`, `assignee`, `opener`, `attachments`, `has_unread_replies`. Template variables in reply bodies are resolved.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    put:
      summary: 'Update a ticket.'
      operationId: updateATicket
      description: 'Update a ticket. Only the fields present in the body are updated.'
      parameters: []
      responses:
        201:
          description: 'Updated ticket object with replies.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                subject:
                  type: string
                  description: 'Ticket subject (HTML stripped)'
                  example: string
                body:
                  type: string
                  description: 'Ticket body (HTML sanitized)'
                  example: string
                ticketable_type:
                  type: string
                  description: 'Polymorphic type'
                  example: string
                ticketable_id:
                  type: integer
                  description: 'Entity ID'
                  example: 1
                team_id:
                  type: integer
                  description: 'Support team'
                  example: 1
                priority_id:
                  type: integer
                  description: Priority
                  example: 1
                ticket_category_id:
                  type: integer
                  description: Category
                  example: 1
                ticket_sub_category_id:
                  type: integer
                  description: Sub-category
                  example: 1
                status_id:
                  type: integer
                  description: Status
                  example: 1
                tags:
                  type: array
                  description: Tags
                  example:
                    - string
                  items:
                    type: string
                estimated_resolution_date:
                  type: string
                  description: 'Format: `Y-m-d H:i:s`'
                  example: string
                assignee_id:
                  type: integer
                  description: 'Assignee user'
                  example: 1
                opened_at:
                  type: string
                  description: 'Format: `Y-m-d H:i:s`'
                  example: string
                contract_id:
                  type: integer
                  description: 'Support contract'
                  example: 1
                close:
                  type: boolean
                  description: 'If `true`, sets `open=false` and `closed_at=now()`'
                  example: true
    delete:
      summary: 'Delete a ticket and all its replies.'
      operationId: deleteATicketAndAllItsReplies
      description: 'Delete a ticket and all its replies.'
      parameters: []
      responses:
        200:
          description: '`{"response": "element deleted"}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/tickets/counts:
    get:
      summary: 'Get ticket counts summary for the current day and week.'
      operationId: getTicketCountsSummaryForTheCurrentDayAndWeek
      description: 'Get ticket counts summary for the current day and week.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  total: 150
                  open: 42
                  unassigned: 5
                  assigned_to_myself: 12
                  daily: 8
                  weekly: 30
                  closed: 108
                properties:
                  total:
                    type: integer
                    example: 150
                  open:
                    type: integer
                    example: 42
                  unassigned:
                    type: integer
                    example: 5
                  assigned_to_myself:
                    type: integer
                    example: 12
                  daily:
                    type: integer
                    example: 8
                  weekly:
                    type: integer
                    example: 30
                  closed:
                    type: integer
                    example: 108
      tags:
        - Tickets
  /api/v1/tickets/search:
    get:
      summary: 'Search and filter tickets using Spatie QueryBuilder.'
      operationId: searchAndFilterTicketsUsingSpatieQueryBuilder
      description: 'Search and filter tickets using Spatie QueryBuilder.'
      parameters:
        -
          in: query
          name: 'filter[number]'
          description: 'Filter by ticket number (partial match)'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by ticket number (partial match)'
            example: string
        -
          in: query
          name: 'filter[email]'
          description: 'Filter by email (partial match)'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by email (partial match)'
            example: string
        -
          in: query
          name: 'filter[body]'
          description: 'Filter by body (partial match)'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by body (partial match)'
            example: string
        -
          in: query
          name: 'filter[ticketable_type]'
          description: 'Exact match on ticketable type'
          example: string
          required: false
          schema:
            type: string
            description: 'Exact match on ticketable type'
            example: string
        -
          in: query
          name: 'filter[ticketable_id]'
          description: 'Exact match on ticketable ID'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Exact match on ticketable ID'
            example: 1
        -
          in: query
          name: 'filter[assignee_id]'
          description: 'Exact match on assignee'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Exact match on assignee'
            example: 1
        -
          in: query
          name: 'filter[status_id]'
          description: 'Exact match on status'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Exact match on status'
            example: 1
        -
          in: query
          name: 'filter[priority_id]'
          description: 'Exact match on priority'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Exact match on priority'
            example: 1
        -
          in: query
          name: 'filter[team_id]'
          description: 'Exact match on team'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Exact match on team'
            example: 1
        -
          in: query
          name: 'filter[open]'
          description: 'Scope: open tickets only'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Scope: open tickets only'
            example: true
        -
          in: query
          name: 'filter[closed]'
          description: 'Scope: closed tickets only'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Scope: closed tickets only'
            example: true
        -
          in: query
          name: 'filter[my_tickets]'
          description: 'Scope: tickets assigned to the authenticated user'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Scope: tickets assigned to the authenticated user'
            example: true
        -
          in: query
          name: 'filter[my_teams_tickets]'
          description: "Scope: tickets belonging to the authenticated user's teams"
          example: true
          required: false
          schema:
            type: boolean
            description: "Scope: tickets belonging to the authenticated user's teams"
            example: true
      responses:
        200:
          description: 'Filtered array of ticket objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
  /api/v1/tickets/invoicing:
    post:
      summary: 'Create a billing ticket (used to record time spent and deduct from a support contract).'
      operationId: createABillingTicketusedToRecordTimeSpentAndDeductFromASupportContract
      description: 'Create a billing ticket (used to record time spent and deduct from a support contract). The ticket is created as already closed.'
      parameters:
        -
          in: query
          name: ticketable_type
          description: 'Currently only `App\Customer`'
          example: string
          required: true
          schema:
            type: string
            description: 'Currently only `App\Customer`'
            example: string
        -
          in: query
          name: ticketable_id
          description: 'Customer ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Customer ID'
            example: 1
      responses:
        201:
          description: '`{"response": "ticket created", "ticket": {...}}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                time:
                  type: integer
                  description: 'Time in minutes (must be >= 1)'
                  example: 1
                reference:
                  type: string
                  description: 'Reference string for the ticket subject'
                  example: string
                ticket_date:
                  type: string
                  description: 'Date for the ticket (format: `Y-m-d`). Defaults to now'
                  example: string
                contact_id:
                  type: integer
                  description: 'Contact ID'
                  example: 1
              required:
                - time
  '/api/v1/tickets/assign/{id}':
    put:
      summary: 'Assign a ticket to a user.'
      operationId: assignATicketToAUser
      description: 'Assign a ticket to a user.'
      parameters: []
      responses:
        201:
          description: 'Assignee user object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Missing `assignee_id`.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Ticket or assignee not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                assignee_id:
                  type: integer
                  description: 'ID of the user to assign'
                  example: 1
              required:
                - assignee_id
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/free/{id}':
    put:
      summary: 'Remove the assignee from a ticket.'
      operationId: removeTheAssigneeFromATicket
      description: 'Remove the assignee from a ticket.'
      parameters: []
      responses:
        201:
          description: '`{"response": "ticket freed"}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/close/{id}':
    put:
      summary: 'Close a ticket.'
      operationId: closeATicket
      description: 'Close a ticket.'
      parameters: []
      responses:
        201:
          description: '`{"response": "ticket closed"}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/open/{id}':
    put:
      summary: 'Re-open a ticket.'
      operationId: reOpenATicket
      description: 'Re-open a ticket. If the ticket has a pending closure, calls `reopen` instead (cancels the closure).'
      parameters: []
      responses:
        201:
          description: '`{"response": "ticket open"}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/reopen/{id}':
    put:
      summary: 'Reopen a ticket by cancelling any pending closure.'
      operationId: reopenATicketByCancellingAnyPendingClosure
      description: 'Reopen a ticket by cancelling any pending closure.'
      parameters: []
      responses:
        201:
          description: 'Refreshed ticket object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/amend/{id}':
    put:
      summary: 'Amend the tracked time on a ticket.'
      operationId: amendTheTrackedTimeOnATicket
      description: 'Amend the tracked time on a ticket.'
      parameters: []
      responses:
        201:
          description: '`{"response": "ticket time amended"}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                time:
                  type: string
                  description: 'Time adjustment in minutes'
                  example: string
              required:
                - time
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/export/{customer_id}':
    put:
      summary: 'Export tickets for a customer to an Excel file.'
      operationId: exportTicketsForACustomerToAnExcelFile
      description: 'Export tickets for a customer to an Excel file.'
      parameters:
        -
          in: query
          name: with_segments
          description: 'If `true`, includes time segments in the export'
          example: true
          required: false
          schema:
            type: boolean
            description: 'If `true`, includes time segments in the export'
            example: true
      responses:
        200:
          description: 'Excel file download.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Customer not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: customer_id
        description: 'customer_id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/{id}/read-replies':
    post:
      summary: 'Mark all unread replies of a ticket as read (sets `seen_at = now()`).'
      operationId: markAllUnreadRepliesOfATicketAsReadsetsseenAtNow
      description: 'Mark all unread replies of a ticket as read (sets `seen_at = now()`).'
      parameters: []
      responses:
        201:
          description: '`{"success": true}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/{id}/customer':
    get:
      summary: 'Get the customer linked to a ticket (follows the ticketable relationship chain).'
      operationId: getTheCustomerLinkedToATicketfollowsTheTicketableRelationshipChain
      description: 'Get the customer linked to a ticket (follows the ticketable relationship chain).'
      parameters: []
      responses:
        200:
          description: 'Customer object with contacts.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/tickets/{id}/associates':
    put:
      summary: 'Manage the contacts associated with a ticket (attach and detach).'
      operationId: manageTheContactsAssociatedWithATicketattachAndDetach
      description: 'Manage the contacts associated with a ticket (attach and detach).'
      parameters: []
      responses:
        201:
          description: '`{"response": "N associated and M unassociated"}`'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                associated:
                  type: array
                  description: 'Array of contact IDs to attach'
                  example:
                    - string
                  items:
                    type: string
                unassociated:
                  type: array
                  description: 'Array of contact IDs to detach'
                  example:
                    - string
                  items:
                    type: string
              required:
                - associated
                - unassociated
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/replies:
    get:
      summary: 'List all replies for a ticket.'
      operationId: listAllRepliesForATicket
      description: 'List all replies for a ticket.'
      parameters:
        -
          in: query
          name: ticket_id
          description: 'Ticket ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Ticket ID'
            example: 1
      responses:
        200:
          description: 'Array of reply objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Ticket not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
    post:
      summary: 'Add a reply to a ticket.'
      operationId: addAReplyToATicket
      description: 'Add a reply to a ticket.'
      parameters:
        -
          in: query
          name: ticket_id
          description: 'Ticket ID'
          example: 1
          required: true
          schema:
            type: integer
            description: 'Ticket ID'
            example: 1
      responses:
        201:
          description: 'Created reply object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Ticket not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Tickets
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body:
                  type: string
                  description: 'Reply body (HTML, sanitized on save)'
                  example: string
                close:
                  type: boolean
                  description: 'If `true`, closes the ticket after the reply'
                  example: true
                send_to_assignee:
                  type: boolean
                  description: 'Whether to notify the assignee'
                  example: true
                send_to_contacts:
                  type: boolean
                  description: "Whether to send the reply to the ticket's contacts"
                  example: true
              required:
                - body
                - close
                - send_to_assignee
                - send_to_contacts
  /api/v1/training/courses:
    get:
      summary: 'List all courses.'
      operationId: listAllCourses
      description: 'List all courses.'
      parameters: []
      responses:
        200:
          description: 'Array of course objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    post:
      summary: 'Create a course.'
      operationId: createACourse
      description: 'Create a course.'
      parameters: []
      responses:
        201:
          description: 'Created course object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
  '/api/v1/training/courses/{id}':
    get:
      summary: 'Show a course with lessons, materials, quizzes, programs, and students.'
      operationId: showACourseWithLessonsMaterialsQuizzesProgramsAndStudents
      description: 'Show a course with lessons, materials, quizzes, programs, and students.'
      parameters: []
      responses:
        200:
          description: 'Course object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    put:
      summary: 'Update a course.'
      operationId: updateACourse
      description: 'Update a course.'
      parameters: []
      responses:
        200:
          description: 'Updated course object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        422:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    delete:
      summary: 'Delete a course.'
      operationId: deleteACourse
      description: 'Delete a course.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/training/programs:
    get:
      summary: 'List all programs.'
      operationId: listAllPrograms
      description: 'List all programs.'
      parameters: []
      responses:
        200:
          description: 'Array of program objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    post:
      summary: 'Create a program.'
      operationId: createAProgram
      description: 'Create a program. Supports attaching courses with order via `courses` (array of IDs).'
      parameters: []
      responses:
        201:
          description: 'Created program object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
  '/api/v1/training/programs/{id}':
    get:
      summary: 'Show a program with courses (with pivot `order`), materials, quizzes, and students.'
      operationId: showAProgramWithCourseswithPivotorderMaterialsQuizzesAndStudents
      description: 'Show a program with courses (with pivot `order`), materials, quizzes, and students.'
      parameters: []
      responses:
        200:
          description: 'Program object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    put:
      summary: 'Update a program.'
      operationId: updateAProgram
      description: 'Update a program.'
      parameters: []
      responses:
        200:
          description: 'Updated program object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    delete:
      summary: 'Delete a program.'
      operationId: deleteAProgram
      description: 'Delete a program.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/training/lessons:
    get:
      summary: 'List lessons.'
      operationId: listLessons
      description: 'List lessons. Filterable by `?course_id=`.'
      parameters: []
      responses:
        200:
          description: 'Array of lesson objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
  /api/v1/training/materials:
    get:
      summary: 'List materials.'
      operationId: listMaterials
      description: 'List materials. Filterable by `?course_id=`, `?lesson_id=`, or `?contact_id=`.'
      parameters: []
      responses:
        200:
          description: 'Array of material objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    post:
      summary: 'Upload or create a material.'
      operationId: uploadOrCreateAMaterial
      description: 'Upload or create a material.'
      parameters: []
      responses:
        201:
          description: 'Created material object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
  '/api/v1/training/materials/{id}':
    get:
      summary: 'Show a material.'
      operationId: showAMaterial
      description: 'Show a material.'
      parameters: []
      responses:
        200:
          description: 'Material object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    put:
      summary: 'Update a material.'
      operationId: updateAMaterial
      description: 'Update a material. Supports updating course/lesson/contact associations.'
      parameters: []
      responses:
        200:
          description: 'Updated material object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    delete:
      summary: 'Delete a material and remove its file from storage.'
      operationId: deleteAMaterialAndRemoveItsFileFromStorage
      description: 'Delete a material and remove its file from storage.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/training/materials/{id}/content':
    get:
      summary: 'Download the material file.'
      operationId: downloadTheMaterialFile
      description: 'Download the material file.'
      parameters: []
      responses:
        200:
          description: 'File stream.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/training/quizzes:
    get:
      summary: 'List all quizzes.'
      operationId: listAllQuizzes
      description: 'List all quizzes.'
      parameters: []
      responses:
        200:
          description: 'Array of quiz objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    post:
      summary: 'Create a quiz.'
      operationId: createAQuiz
      description: 'Create a quiz. A default group is automatically created.'
      parameters: []
      responses:
        201:
          description: 'Created quiz object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
  '/api/v1/training/quizzes/{id}':
    get:
      summary: 'Show a quiz with groups and questions.'
      operationId: showAQuizWithGroupsAndQuestions
      description: 'Show a quiz with groups and questions.'
      parameters: []
      responses:
        200:
          description: 'Quiz object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    put:
      summary: 'Update a quiz.'
      operationId: updateAQuiz
      description: 'Update a quiz.'
      parameters: []
      responses:
        200:
          description: 'Updated quiz object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    delete:
      summary: 'Delete a quiz.'
      operationId: deleteAQuiz
      description: 'Delete a quiz.'
      parameters: []
      responses:
        200:
          description: Success.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/training/quiz-questions/types:
    get:
      summary: 'Get the list of available question types.'
      operationId: getTheListOfAvailableQuestionTypes
      description: 'Get the list of available question types.'
      parameters: []
      responses:
        200:
          description: 'Array of type strings. Available types: `single_choice`, `multiple_choice`, `true_false`, `short_answer`, `essay`.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
  /api/v1/training/quiz-questions:
    get:
      summary: 'List questions.'
      operationId: listQuestions
      description: 'List questions. Filterable by `?group_id=`.'
      parameters: []
      responses:
        200:
          description: 'Array of question objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
    post:
      summary: 'Create a question.'
      operationId: createAQuestion
      description: 'Create a question. `type` must be one of the allowed types.'
      parameters: []
      responses:
        201:
          description: 'Created question object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - Training
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type:
                  type: string
                  description: 'Question type'
                  example: string
                label:
                  type: string
                  description: 'Question text'
                  example: string
                group_id:
                  type: integer
                  description: 'FK to quiz group'
                  example: 1
                points:
                  type: string
                  description: 'Points awarded for a correct answer'
                  example: string
              required:
                - type
                - label
                - group_id
  /api/v1/users:
    get:
      summary: 'List all users with filtering, sorting, and pagination.'
      operationId: listAllUsersWithFilteringSortingAndPagination
      description: 'List all users with filtering, sorting, and pagination.'
      parameters:
        -
          in: query
          name: 'filter[name]'
          description: 'Filter by name'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by name'
            example: string
        -
          in: query
          name: 'filter[email]'
          description: 'Filter by email'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by email'
            example: string
        -
          in: query
          name: 'filter[active]'
          description: 'Filter active/inactive'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter active/inactive'
            example: true
        -
          in: query
          name: 'filter[role]'
          description: 'Filter by role level'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Filter by role level'
            example: 1
        -
          in: query
          name: 'filter[phone]'
          description: 'Filter by phone'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by phone'
            example: string
        -
          in: query
          name: 'filter[mobile_phone]'
          description: 'Filter by mobile phone'
          example: string
          required: false
          schema:
            type: string
            description: 'Filter by mobile phone'
            example: string
        -
          in: query
          name: 'filter[manager]'
          description: 'Filter managers'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter managers'
            example: true
        -
          in: query
          name: 'filter[technical_manager]'
          description: 'Filter technical managers'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter technical managers'
            example: true
        -
          in: query
          name: 'filter[sales]'
          description: 'Filter sales users'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter sales users'
            example: true
        -
          in: query
          name: 'filter[technical]'
          description: 'Filter technical users'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter technical users'
            example: true
        -
          in: query
          name: 'filter[support_team]'
          description: 'Filter support team members'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter support team members'
            example: true
        -
          in: query
          name: 'filter[sales_admin]'
          description: 'Filter sales admin users'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter sales admin users'
            example: true
        -
          in: query
          name: 'filter[admin]'
          description: 'Filter admin users'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter admin users'
            example: true
        -
          in: query
          name: 'filter[business_finder]'
          description: 'Filter business finders'
          example: true
          required: false
          schema:
            type: boolean
            description: 'Filter business finders'
            example: true
        -
          in: query
          name: 'filter[term_match]'
          description: 'Full-text term match'
          example: string
          required: false
          schema:
            type: string
            description: 'Full-text term match'
            example: string
        -
          in: query
          name: sort
          description: 'Sort field (`id`, `name`, `role`, `created_at`, `updated_at`)'
          example: string
          required: false
          schema:
            type: string
            description: 'Sort field (`id`, `name`, `role`, `created_at`, `updated_at`)'
            example: string
        -
          in: query
          name: per_page
          description: 'Items per page (default 10)'
          example: 1
          required: false
          schema:
            type: integer
            description: 'Items per page (default 10)'
            example: 1
      responses:
        200:
          description: 'Paginated list of user objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    post:
      summary: 'Create a new user (standard resource route).'
      operationId: createANewUserstandardResourceRoute
      description: 'Create a new user (standard resource route). Admin only.'
      parameters: []
      responses:
        201:
          description: 'Created user object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
  '/api/v1/users/{id}':
    get:
      summary: 'Get a single user by ID.'
      operationId: getASingleUserByID
      description: 'Get a single user by ID. Includes `teams` and `support_teams` (if ticketing module enabled).'
      parameters: []
      responses:
        200:
          description: 'User object with teams.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    put:
      summary: 'Update a user.'
      operationId: updateAUser
      description: 'Update a user.'
      parameters: []
      responses:
        200:
          description: 'Updated user object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    delete:
      summary: 'Delete a user.'
      operationId: deleteAUser
      description: 'Delete a user.'
      parameters: []
      responses:
        200:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    parameters:
      -
        in: path
        name: id
        description: 'User ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/v1/impersonate:
    post:
      summary: 'Impersonate another user (admin only).'
      operationId: impersonateAnotherUseradminOnly
      description: 'Impersonate another user (admin only).'
      parameters: []
      responses:
        200:
          description: 'Impersonation session established.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id:
                  type: integer
                  description: 'ID of user to impersonate'
                  example: 1
              required:
                - user_id
    get:
      summary: 'Leave impersonation and return to original user.'
      operationId: leaveImpersonationAndReturnToOriginalUser
      description: 'Leave impersonation and return to original user.'
      parameters: []
      responses:
        200:
          description: 'Original session restored.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
  /api/v1/teams:
    get:
      summary: 'List all teams ordered by name (no pagination).'
      operationId: listAllTeamsOrderedByNamenoPagination
      description: 'List all teams ordered by name (no pagination).'
      parameters: []
      responses:
        200:
          description: 'Array of all team objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    post:
      summary: 'Create a new team.'
      operationId: createANewTeam
      description: 'Create a new team.'
      parameters: []
      responses:
        201:
          description: 'Created team object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: 'Validation error.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
  '/api/v1/teams/{id}':
    get:
      summary: 'Get a single team by ID with `users` relation.'
      operationId: getASingleTeamByIDWithusersRelation
      description: 'Get a single team by ID with `users` relation.'
      parameters: []
      responses:
        200:
          description: 'Team object with users.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    put:
      summary: 'Update a team.'
      operationId: updateATeam
      description: 'Update a team.'
      parameters: []
      responses:
        201:
          description: 'Updated team object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        400:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: Error.
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    delete:
      summary: 'Delete a team.'
      operationId: deleteATeam
      description: 'Delete a team.'
      parameters: []
      responses:
        201:
          description: 'Success message.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Users and Teams'
    parameters:
      -
        in: path
        name: id
        description: 'Team ID'
        example: 1
        required: true
        schema:
          type: integer
  /api/version:
    get:
      summary: 'Get API version and status information.'
      operationId: getAPIVersionAndStatusInformation
      description: 'Get API version and status information.'
      parameters: []
      responses:
        200:
          description: ''
          content:
            application/json:
              schema:
                type: object
                example:
                  version: '1.0'
                  status: ok
                  vendor: blue_rock_tel
                  url: 'https://bluerocktel.com'
                properties:
                  version:
                    type: string
                    example: '1.0'
                  status:
                    type: string
                    example: ok
                  vendor:
                    type: string
                    example: blue_rock_tel
                  url:
                    type: string
                    example: 'https://bluerocktel.com'
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/services:
    get:
      summary: 'Get external services configuration (BlueRockTEL, Hetzner, OVH Cloud, etc.).'
      operationId: getExternalServicesConfigurationBlueRockTELHetznerOVHCloudEtc
      description: 'Get external services configuration (BlueRockTEL, Hetzner, OVH Cloud, etc.).'
      parameters: []
      responses:
        200:
          description: 'JSON object with services grouped by provider.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/base_url:
    post:
      summary: 'Get the API base URL for a given client origin.'
      operationId: getTheAPIBaseURLForAGivenClientOrigin
      description: 'Get the API base URL for a given client origin. Used by frontend apps to resolve the correct API URL.'
      parameters: []
      responses:
        200:
          description: 'Object mapping origin to API base URL.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Origin not in config.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/health-report:
    get:
      summary: 'Get an application health report.'
      operationId: getAnApplicationHealthReport
      description: 'Get an application health report.'
      parameters: []
      responses:
        200:
          description: 'Health status data.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/v1/datetime:
    post:
      summary: 'Test date/timezone conversion.'
      operationId: testDatetimezoneConversion
      description: 'Test date/timezone conversion.'
      parameters: []
      responses:
        200:
          description: 'Converted datetime result.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/v1/colors:
    get:
      summary: 'List available color options (for UI labeling).'
      operationId: listAvailableColorOptionsforUILabeling
      description: 'List available color options (for UI labeling).'
      parameters: []
      responses:
        200:
          description: 'Array of color objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/payment-methods:
    get:
      summary: 'List all payment methods (seeded/read-only).'
      operationId: listAllPaymentMethodsseededreadOnly
      description: 'List all payment methods (seeded/read-only).'
      parameters: []
      responses:
        200:
          description: 'Array of payment method objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  '/api/v1/payment-methods/{id}':
    get:
      summary: 'Get a single payment method by ID.'
      operationId: getASinglePaymentMethodByID
      description: 'Get a single payment method by ID.'
      parameters: []
      responses:
        200:
          description: 'Payment method object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        404:
          description: 'Not found.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
    parameters:
      -
        in: path
        name: id
        description: 'id identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/modules:
    get:
      summary: 'List all installed/enabled application modules.'
      operationId: listAllInstalledenabledApplicationModules
      description: 'List all installed/enabled application modules.'
      parameters: []
      responses:
        200:
          description: 'Array of module objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/v1/click-to-call/to:
    get:
      summary: 'Get the target number for a click-to-call request.'
      operationId: getTheTargetNumberForAClickToCallRequest
      description: 'Get the target number for a click-to-call request.'
      parameters: []
      responses:
        200:
          description: 'Target call number.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/click-to-call/enabled:
    get:
      summary: 'Check if click-to-call is enabled for the authenticated user.'
      operationId: checkIfClickToCallIsEnabledForTheAuthenticatedUser
      description: 'Check if click-to-call is enabled for the authenticated user.'
      parameters: []
      responses:
        200:
          description: 'Enabled status.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/transformers/correct-text-spelling:
    post:
      summary: 'Correct spelling in a text string (AI-powered).'
      operationId: correctSpellingInATextStringAIPowered
      description: 'Correct spelling in a text string (AI-powered).'
      parameters: []
      responses:
        200:
          description: 'Corrected text.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                text:
                  type: string
                  description: 'Text to correct'
                  example: string
              required:
                - text
  /api/v1/ovh-cloud-projects:
    get:
      summary: 'List all OVH Cloud projects.'
      operationId: listAllOVHCloudProjects
      description: 'List all OVH Cloud projects.'
      parameters: []
      responses:
        200:
          description: 'Array of OVH Cloud project objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  '/api/v1/ovh-cloud-projects/{project}':
    get:
      summary: 'Get a single OVH Cloud project.'
      operationId: getASingleOVHCloudProject
      description: 'Get a single OVH Cloud project.'
      parameters: []
      responses:
        200:
          description: 'Project object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
    parameters:
      -
        in: path
        name: project
        description: 'project identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/ovh-cloud-projects/{project}/instances':
    get:
      summary: 'List instances (servers) in an OVH Cloud project.'
      operationId: listInstancesserversInAnOVHCloudProject
      description: 'List instances (servers) in an OVH Cloud project.'
      parameters: []
      responses:
        200:
          description: 'Array of instance objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
    parameters:
      -
        in: path
        name: project
        description: 'project identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/ovh-cloud-projects/{project}/storages':
    get:
      summary: 'List object storage containers in an OVH Cloud project.'
      operationId: listObjectStorageContainersInAnOVHCloudProject
      description: 'List object storage containers in an OVH Cloud project.'
      parameters: []
      responses:
        200:
          description: 'Array of storage objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
    parameters:
      -
        in: path
        name: project
        description: 'project identifier.'
        example: string
        required: true
        schema:
          type: string
  '/api/v1/ovh-cloud-projects/{project}/entity':
    get:
      summary: 'Get the BlueRockTEL entity (customer/host) linked to an OVH Cloud project.'
      operationId: getTheBlueRockTELEntitycustomerhostLinkedToAnOVHCloudProject
      description: 'Get the BlueRockTEL entity (customer/host) linked to an OVH Cloud project.'
      parameters: []
      responses:
        200:
          description: 'Entity object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
    parameters:
      -
        in: path
        name: project
        description: 'project identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/get_quick:
    get:
      summary: 'Perform a quick global search across customers, contacts, etc.'
      operationId: performAQuickGlobalSearchAcrossCustomersContactsEtc
      description: 'Perform a quick global search across customers, contacts, etc.'
      parameters:
        -
          in: query
          name: term
          description: 'Search term'
          example: string
          required: false
          schema:
            type: string
            description: 'Search term'
            example: string
      responses:
        200:
          description: 'Grouped search results.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/customer:
    get:
      summary: 'Get the current customer context for the authenticated session (used in web integrations).'
      operationId: getTheCurrentCustomerContextForTheAuthenticatedSessionusedInWebIntegrations
      description: 'Get the current customer context for the authenticated session (used in web integrations).'
      parameters: []
      responses:
        200:
          description: 'Customer data.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/store_customer_support_survey_record:
    post:
      summary: 'Store a customer support survey response.'
      operationId: storeACustomerSupportSurveyResponse
      description: 'Store a customer support survey response.'
      parameters: []
      responses:
        200:
          description: 'Created survey record.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
        201:
          description: 'Created survey record.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/invoice-units:
    post:
      summary: 'Admin utility to look up invoice unit data.'
      operationId: adminUtilityToLookUpInvoiceUnitData
      description: 'Admin utility to look up invoice unit data. Primarily for the BlueRockTEL internal team.'
      parameters: []
      responses:
        200:
          description: 'Invoice unit data.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/sftp_accounts:
    get:
      summary: 'List all SFTP accounts.'
      operationId: listAllSFTPAccounts
      description: 'List all SFTP accounts.'
      parameters: []
      responses:
        200:
          description: 'Array of SFTP account objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/sftp_accounts/available:
    get:
      summary: 'List available (unassigned) SFTP accounts.'
      operationId: listAvailableunassignedSFTPAccounts
      description: 'List available (unassigned) SFTP accounts.'
      parameters: []
      responses:
        200:
          description: 'Array of available SFTP account objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  '/api/v1/sftp_accounts/{sftp_account}':
    put:
      summary: 'Update an SFTP account.'
      operationId: updateAnSFTPAccount
      description: 'Update an SFTP account.'
      parameters: []
      responses:
        201:
          description: 'Updated SFTP account.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
    parameters:
      -
        in: path
        name: sftp_account
        description: 'sftp_account identifier.'
        example: string
        required: true
        schema:
          type: string
  /api/v1/customer_concerns/search:
    get:
      summary: 'Search customer concerns.'
      operationId: searchCustomerConcerns
      description: 'Search customer concerns.'
      parameters:
        -
          in: query
          name: term
          description: 'Search term'
          example: string
          required: false
          schema:
            type: string
            description: 'Search term'
            example: string
      responses:
        200:
          description: 'Array of matching concern objects.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/v1/customer_concerns:
    post:
      summary: 'Create a new customer concern.'
      operationId: createANewCustomerConcern
      description: 'Create a new customer concern.'
      parameters: []
      responses:
        201:
          description: 'Created concern.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/tiny-drive:
    post:
      summary: 'Get a TinyMCE TinyDrive authentication token for the rich text editor.'
      operationId: getATinyMCETinyDriveAuthenticationTokenForTheRichTextEditor
      description: 'Get a TinyMCE TinyDrive authentication token for the rich text editor.'
      parameters: []
      responses:
        200:
          description: 'Token object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
  /api/v1/pictures:
    post:
      summary: 'Upload a picture/image.'
      operationId: uploadAPictureimage
      description: 'Upload a picture/image.'
      parameters: []
      responses:
        201:
          description: 'Stored picture URL/object.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
  /api/count:
    get:
      summary: 'Get a count of bills (internal use, likely for billing service health checks).'
      operationId: getACountOfBillsinternalUseLikelyForBillingServiceHealthChecks
      description: 'Get a count of bills (internal use, likely for billing service health checks).'
      parameters: []
      responses:
        200:
          description: 'Count value.'
          content:
            application/json:
              schema:
                type: object
                example: {  }
                properties: {  }
      tags:
        - 'Utilities and Helpers'
      security: []
