openapi: 3.1.0
info:
  title: Solar Assistant Cloud API
  version: "1"
  description: |
    The Solar Assistant Cloud API covers user and site administration as well as
    real-time metrics streamed from devices through the proxy.

    Generate an API token on your user details page (https://solar-assistant.io/user/edit#api)
    and pass it as a Bearer token in the Authorization header.

    Full documentation: https://solar-assistant.io/help/integration/cloud-api

servers:
  - url: https://solar-assistant.io
    description: Cloud API
  - url: https://{proxy_host}
    description: Proxy pass-through (host from the authorize response)
    variables:
      proxy_host:
        default: us-htz-1.solar-assistant.io
        description: Proxy hostname returned by POST /api/v1/sites/{id}/authorize

security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
    proxyAuth:
      type: http
      scheme: bearer
      description: Bearer token from the authorize response.

  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        phone_number:
          type: string
        locale:
          type: string

    SiteMember:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        role:
          type: string
          enum: [owner, admin, member]

    SiteSummary:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        proxy:
          type: string
        last_seen_at:
          type: string
          format: date-time

    Site:
      allOf:
        - $ref: "#/components/schemas/SiteSummary"
        - type: object
          properties:
            owner:
              $ref: "#/components/schemas/SiteMember"
            organization:
              type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
                cloud_host:
                  type: string
            users:
              type: array
              items:
                $ref: "#/components/schemas/SiteMember"

    SiteAuthorization:
      type: object
      properties:
        host:
          type: string
          description: Proxy hostname to use for device requests.
        site_id:
          type: integer
        site_name:
          type: string
        site_key:
          type: string
        token:
          type: string
          description: Bearer token for proxy requests. Expires after 7 days.

paths:
  /api/v1/user:
    get:
      summary: Get current user
      description: Returns the authenticated user's profile.
      servers:
        - url: https://solar-assistant.io
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

  /api/v1/sites:
    get:
      summary: List sites
      description: Returns all sites associated with the authenticated account.
      servers:
        - url: https://solar-assistant.io
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SiteSummary"

  /api/v1/sites/register:
    post:
      summary: Register a site
      description: |
        Claims an unregistered Solar Assistant device and associates it with the
        authenticated account. If the site is already registered to this account,
        the existing site is returned.
      servers:
        - url: https://solar-assistant.io
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [uid]
              properties:
                uid:
                  type: string
                  description: Unique identifier of the device.
                name:
                  type: string
                description:
                  type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Site"

  /api/v1/sites/{id}:
    get:
      summary: Show site
      description: Returns full details for a site including its members and organization.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Site"

  /api/v1/sites/{id}/users:
    get:
      summary: List site members
      description: Returns all members of a site with their roles.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SiteMember"
    post:
      summary: Invite or update a site member
      description: |
        Invites a user by email. If the email belongs to an existing account they
        are linked immediately; otherwise they receive an invitation email.
        Posting with an existing member's email updates their role.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, role]
              properties:
                email:
                  type: string
                role:
                  type: string
                  enum: [member, admin, owner]
                first_name:
                  type: string
                  description: Required when inviting a new user.
                last_name:
                  type: string
                  description: Required when inviting a new user.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SiteMember"

  /api/v1/sites/{id}/users/{user_id}:
    patch:
      summary: Update a site member's role
      description: Requires admin or owner access on the site.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: user_id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [role]
              properties:
                role:
                  type: string
                  enum: [member, admin, owner]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SiteMember"
    delete:
      summary: Remove a site member
      description: Removes a member from the site. The site owner cannot be removed.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - name: user_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "204":
          description: No Content

  /api/v1/sites/{id}/authorize:
    post:
      summary: Generate site access token
      description: |
        Returns temporary credentials for direct access to a site through the
        cloud proxy. Use the returned host, token, and site_key as headers on
        every proxy request. The token expires after 7 days.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SiteAuthorization"

  /api/v1/users:
    get:
      summary: List users
      description: Returns all users. Only available to users with a registered organization.
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"

  /api/v1/sign_in:
    post:
      summary: Sign in
      description: |
        Authenticates a user and returns an API token. Pass organization_id to
        scope the session to your organization. Also accepts a pending_token in
        place of a password to complete sign-in after email confirmation.

        Returns HTTP 412 when the account exists but email is not yet confirmed.
        The response includes a pending_token; poll this endpoint with it until
        the user clicks the confirmation link.
      security: []
      servers:
        - url: https://solar-assistant.io
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, organization_id]
              properties:
                email:
                  type: string
                password:
                  type: string
                  description: Required unless using pending_token.
                pending_token:
                  type: string
                  description: Token from Register. Use instead of password after email confirmation.
                organization_id:
                  type: integer
      responses:
        "200":
          description: Authenticated
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                  expires_at:
                    type: string
                    format: date-time
                  user:
                    $ref: "#/components/schemas/User"
        "412":
          description: Email not yet confirmed — poll with pending_token
          content:
            application/json:
              schema:
                type: object
                properties:
                  pending_token:
                    type: string

  /api/v1/password/reset:
    post:
      summary: Request password reset
      description: |
        Sends a password reset email to the user. Always returns 200 regardless
        of whether the email exists, to avoid enumeration. Pass organization_id
        to brand the email for your organization.
      security: []
      servers:
        - url: https://solar-assistant.io
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                organization_id:
                  type: integer
      responses:
        "200":
          description: OK

  /api/v1/password/reset/{token}:
    put:
      summary: Set new password
      description: |
        Sets a new password using the token from the reset email. On success
        returns an API token so the user is immediately signed in.
      security: []
      servers:
        - url: https://solar-assistant.io
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
          description: Token from the password reset email.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [password]
              properties:
                password:
                  type: string
                organization_id:
                  type: integer
                  description: Scopes the returned API token to your organization.
      responses:
        "200":
          description: Password reset and signed in
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                  expires_at:
                    type: string
                    format: date-time
                  user:
                    $ref: "#/components/schemas/User"
        "422":
          description: Invalid token or validation error

  /api/v1/register:
    post:
      summary: Register a new user account
      description: |
        Creates a new user account and sends a confirmation email. Only available
        to users with a registered organization.

        Registration is a three-step sequence:
        1. Open https://solar-assistant.io/register/verify in a WebView (mobile)
           or iframe (web). The page shows a compact bot-verification widget
           and on completion sends a postMessage with
           { verification_token: "..." }. The token is single-use and expires
           after a few minutes.
        2. POST to this endpoint with email, password, verification_token, and
           organization_id. On success you receive a pending_token.
        3. Once the user clicks the confirmation link in their email, POST to
           /api/v1/sign_in with { email, pending_token }. Poll until it succeeds
           (returns HTTP 412 while unconfirmed).
      security: []
      servers:
        - url: https://solar-assistant.io
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password, verification_token, organization_id]
              properties:
                email:
                  type: string
                password:
                  type: string
                verification_token:
                  type: string
                  description: Bot verification token from /register/verify.
                organization_id:
                  type: integer
                  description: Your organization ID. The confirmation email will be branded for this organization.
      responses:
        "201":
          description: Account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  next_step:
                    type: string
                    example: click_email_link
                  pending_token:
                    type: string
                  user:
                    type: object
                    properties:
                      email:
                        type: string
        "422":
          description: Validation error or verification failed

  /api/v1/metrics:
    get:
      summary: Metrics snapshot
      description: |
        Returns a metrics snapshot from the device via the cloud proxy. This is
        the same Solar Assistant device REST API you would call on the local
        network; all the same paths work the same way.

        Use the host, token, and site_key from POST /api/v1/sites/{id}/authorize.
        See https://solar-assistant.io/help/integration/rest-api for all available paths.
      servers:
        - url: https://{proxy_host}
          variables:
            proxy_host:
              default: us-htz-1.solar-assistant.io
              description: Proxy hostname from the authorize response.
      security:
        - proxyAuth: []
      parameters:
        - name: Site-Id
          in: header
          required: true
          schema:
            type: integer
          description: Site ID from the authorize response.
        - name: Site-Key
          in: header
          required: true
          schema:
            type: string
          description: Site key from the authorize response.
      responses:
        "200":
          description: OK

  /api/websocket:
    get:
      summary: Stream metrics via WebSocket
      description: |
        Streams live metrics using the Solar Assistant WebSocket API through the
        cloud proxy. Connect to wss://{host}/api/websocket with the token as a
        query parameter and Site-Id / Site-Key as headers. After connecting, join
        the metrics channel using the Phoenix Channel V2 protocol.

        See https://solar-assistant.io/help/integration/websocket-api for channel
        events and topic format.
      servers:
        - url: https://{proxy_host}
          variables:
            proxy_host:
              default: us-htz-1.solar-assistant.io
              description: Proxy hostname from the authorize response.
      security: []
      parameters:
        - name: token
          in: query
          required: true
          schema:
            type: string
          description: Bearer token from the authorize response.
        - name: vsn
          in: query
          schema:
            type: string
            default: "2.0.0"
        - name: Site-Id
          in: header
          required: true
          schema:
            type: integer
        - name: Site-Key
          in: header
          required: true
          schema:
            type: string
      responses:
        "101":
          description: Switching Protocols
