openapi: 3.0.3
info:
  title: 1draad API
  version: '1.0'
  description: Read saved account projects and electrical schematics. All account tokens have full API access.
servers:
  - url: https://app.1draad.be/api/v1
  - url: https://app.1filaire.be/api/v1
  - url: https://app.1wire.be/api/v1
security:
  - bearerAuth: []
paths:
  /projects:
    get:
      operationId: listProjects
      summary: List all non-deleted projects in the token's account
      description: Ordered by created_at descending, then id descending. Includes every project owner.
      parameters:
        - name: page
          in: query
          schema: { type: integer, minimum: 1, default: 1 }
        - name: per_page
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
        - name: query
          in: query
          description: Search names, references, EAN and contact/address details. Truncated to 200 characters.
          schema: { type: string }
        - name: updated_since
          in: query
          description: Inclusive timestamp filter. Does not return deletion tombstones.
          schema: { type: string, format: date-time }
      responses:
        '200':
          description: Project summaries and pagination
          content:
            application/json:
              schema:
                type: object
                required: [data, pagination]
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/ProjectSummary' }
                  pagination:
                    type: object
                    required: [page, per_page, total_count, total_pages]
                    properties:
                      page: { type: integer }
                      per_page: { type: integer }
                      total_count: { type: integer }
                      total_pages: { type: integer }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /projects/{project_id}:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
    get:
      operationId: getProject
      summary: Get project details and app/share URLs
      description: A share URL is returned only if sharing is already enabled. Reads never enable sharing.
      responses:
        '200':
          description: Saved project details
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data: { $ref: '#/components/schemas/ProjectDetails' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
  /projects/{project_id}/schematic:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
    get:
      operationId: getProjectSchematic
      summary: Read components, circuit context and annotations
      description: Exact free-text descriptions are preserved. No equipment metadata is inferred. Only saved state is included.
      responses:
        '200':
          description: Readable projection of one saved project version
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: object
                    required: [project_id, updated_at, property_units, schematics]
                    properties:
                      project_id: { type: integer, format: int64 }
                      updated_at: { type: string, format: date-time }
                      property_units:
                        type: object
                        additionalProperties: { type: string }
                        example: { amperage: A, delta_current: mA, cross_section: mm², length: m }
                      schematics:
                        type: array
                        items: { $ref: '#/components/schemas/Schematic' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Create a token via API in the topbar user menu. Send it only in the Authorization header.
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      schema: { type: integer, format: int64 }
  responses:
    BadRequest:
      description: Invalid pagination or ISO 8601 timestamp
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Unauthorized:
      description: A valid bearer API token is required. Browser sessions do not authenticate API requests.
      headers:
        WWW-Authenticate:
          schema: { type: string, example: 'Bearer realm="1draad API"' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Project is missing, deleted or belongs to another account
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string, enum: [invalid_parameters, unauthorized, not_found] }
            message: { type: string }
    ProjectSummary:
      type: object
      required: [id, name, reference, client_name, installation_ean, created_at, updated_at, app_url, share_url]
      properties:
        id: { type: integer, format: int64 }
        name: { type: string }
        reference: { type: string, nullable: true }
        client_name: { type: string, nullable: true }
        installation_ean: { type: string, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        app_url: { type: string, format: uri, description: Requires an authorized signed-in user }
        share_url: { type: string, format: uri, nullable: true }
    ProjectDetails:
      allOf:
        - $ref: '#/components/schemas/ProjectSummary'
        - type: object
          properties:
            account_id: { type: integer, format: int64 }
            owned_by_id: { type: integer, format: int64, nullable: true }
            client_language: { type: string, nullable: true }
            installation_kind: { type: string }
            installation_same_as_client: { type: boolean }
            current_type: { type: string, nullable: true }
            client: { $ref: '#/components/schemas/Contact' }
            installation_location: { $ref: '#/components/schemas/Contact' }
            installer: { $ref: '#/components/schemas/Contact' }
            controller: { $ref: '#/components/schemas/Contact' }
    Contact:
      type: object
      properties:
        name: { type: string, nullable: true }
        street: { type: string, nullable: true }
        postal_code: { type: string, nullable: true }
        city: { type: string, nullable: true }
        country: { type: string, nullable: true }
        company_number: { type: string, nullable: true }
        fixed_phone: { type: string, nullable: true }
        mobile_phone: { type: string, nullable: true }
        email: { type: string, nullable: true }
    Component:
      type: object
      required: [id, type, label, description, properties]
      properties:
        id: { type: string }
        type: { type: string, example: battery }
        label: { type: string, example: Battery }
        description: { type: string, nullable: true, example: 'S/N: BAT-001' }
        properties:
          type: object
          additionalProperties: true
          description: Saved settings with snake_case keys; free-text descriptions are returned separately
        parent_id: { type: string, nullable: true }
        child_ids: { type: array, items: { type: string } }
        connection:
          type: object
          additionalProperties: { type: string }
          description: Saved converter/control ports and branch/backup-board sides and slots, with snake_case keys
        switchboard_id: { type: string, nullable: true }
        circuit_id: { type: string, nullable: true }
        circuit_label: { type: string, nullable: true }
        circuit_name: { type: string, nullable: true }
        phase: { type: string, nullable: true }
        subcircuit_id: { type: string, nullable: true }
        subcircuit_label:
          oneOf:
            - { type: integer, nullable: true }
            - { type: string }
        subcircuit_name: { type: string, nullable: true }
        domotica_branch: { type: string, nullable: true, example: '4.1' }
    Annotation:
      type: object
      properties:
        id: { type: string }
        anchor_component_id: { type: string, nullable: true }
        text: { type: string, nullable: true }
    Schematic:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        root_component_ids: { type: array, items: { type: string } }
        components:
          type: array
          items: { $ref: '#/components/schemas/Component' }
        annotations:
          type: array
          items: { $ref: '#/components/schemas/Annotation' }
        backup_connections:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              converter_component_id: { type: string }
              switchboard_component_id: { type: string }
