openapi: 3.0.3
info:
  title: HeyCharge API
  version: 1.0.0
  description: |-
    You can find more documentation at [HeyCharge Docs](https://developer.heycharge.com).

    ## Authentication

    All endpoints (except `/auth/*`) require a Bearer token obtained from `POST /auth/sign-in`.
    The token expires in 3 hours.

    ## Role-Based Access Control

    Access to endpoints is controlled by the role assigned to the authenticated user.
    Roles are assigned by HeyCharge during onboarding.

    | Role | Read | Write (POST/PATCH/DELETE) |
    |---|---|---|
    | ResellerAdmin | Yes | Yes |
    | DomainManager | Yes | Yes |
    | SiteManager | Yes | Yes |
    | Installer | Yes | Yes |
    | TenantGroupAdmin | Yes | No |
    | B2bMemberGroupMember | Yes | No |

    Endpoints that mutate data (create, update, delete) require one of: ResellerAdmin, DomainManager, SiteManager, or Installer.

    ## Acting on behalf of a B2B (BackofficeAdmin only)

    For regular B2B users the target B2B is locked to the value embedded in their JWT — no override is possible.

    Users with the **BackofficeAdmin** flag can supply a `b2bid` header to target any B2B:

    ```
    GET /v1/b2b/users/installers
    Authorization: Bearer <token>
    b2bid: <target-b2b-id>
    ```

    If the header is omitted, the B2B from the JWT is used as a fallback. Supplying `b2bid` as a regular (non-BackofficeAdmin) user has no effect — the JWT value is always enforced.

  contact:
    name: HeyCharge GmbH
    email: dima@heycharge.com

externalDocs:
  description: HeyCharge GmbH - Website
  url: https://heycharge.com

servers:
  - url: https://api.heycharge.com/v1/b2b
    description: Production server (uses live data)
  - url: https://api.dev.heycharge.com/v1/b2b
    description: Dev server (uses test data)

tags:
  - name: Auth
    description: API user authentication
  - name: Chargers
    description: Charger information and access control
  - name: Sessions
    description: Session information
  - name: Users
    description: User information
  - name: Sites
    description: Sites (physical locations)
  - name: Properties
    description: Sub-properties within a site

paths:
  /auth/sign-in:
    post:
      tags:
        - Auth
      summary: Sign-in to get access token in order to use the API
      operationId: signIn
      security:
        - ApiKeyAuth: []
      requestBody:
        description: User credentials
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserCredentials'
      responses:
        '200':
          description: API tokens
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignInResponse'

  /auth/reset-password:
    post:
      tags:
        - Auth
      summary: A reset password link will be sent to the email provided
      operationId: resetPassword
      security:
        - ApiKeyAuth: []
      requestBody:
        description: Email address to which system will send the reset link
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResetPassword'
      responses:
        '204':
          description: Password reset link sent to the provided address.


  /chargers:
    get:
      tags:
        - Chargers
      summary: Get all chargers
      operationId: getChargers
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
      responses:
        '200':
          description: An array of chargers
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Charger'
        '403':
          description: Forbidden. User does not have a role that grants read access.

  /chargers/{id}:
    get:
      tags:
        - Chargers
      summary: Get charger by its ID
      operationId: getCharger
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: ID of charger to return
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A charger model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Charger'
        '403':
          description: Forbidden. User does not have a role that grants read access.

  /chargers/{id}/access:
    patch:
      tags:
        - Chargers
      summary: Add or remove users' access to the charger
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: updateUserChargers
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: ID of charger to return
          required: true
          schema:
            type: string
      requestBody:
        description: User IDs to add or remove charger access from
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateChargerAccess'
      responses:
        '200':
          description: A charger model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Charger'
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.

  /sessions:
    get:
      tags:
        - Sessions
      summary: Get all charging sessions
      operationId: getSessions
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: start
          in: query
          description: Start timestamp in ISO format
          required: false
          schema:
            type: string
        - name: end
          in: query
          description: End timestamp in ISO format
          required: false
          schema:
            type: string
        - name: userId
          in: query
          description: ID of the user
          required: false
          schema:
            type: string
        - name: chargerId
          in: query
          description: ID of the charger
          required: false
          schema:
            type: string
      responses:
        '200':
          description: An array of sessions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Session'
        '403':
          description: Forbidden. User does not have a role that grants read access.

  /sessions/{id}:
    get:
      tags:
        - Sessions
      summary: Get session by its ID
      operationId: getSession
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: ID of session to return
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A session model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Session'
        '403':
          description: Forbidden. User does not have a role that grants read access.

  /users:
    get:
      tags:
        - Users
      summary: Get all users
      operationId: getUsers
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
      responses:
        '200':
          description: An array of users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
        '403':
          description: Forbidden. User does not have a role that grants read access.
    post:
      tags:
        - Users
      summary: Create user
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: createUser
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
      requestBody:
        description: Details of the user to be created
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
      responses:
        '200':
          description: A user model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.

  /users/installers:
    get:
      tags:
        - Users
      summary: Get all installers for the authenticated user's B2B
      description: Returns all users with installer access to the B2B associated with the authenticated token.
      operationId: getInstallers
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
      responses:
        '200':
          description: An array of installer users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
        '403':
          description: Forbidden. User does not have a role that grants read access.

  /users/{id}:
    get:
      tags:
        - Users
      summary: Get user by the user ID
      operationId: getUser
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: ID of user to return
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A user model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '403':
          description: Forbidden. User does not have a role that grants read access.
    patch:
      tags:
        - Users
      summary: Update user
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: updateUser
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: ID of the user
          required: true
          schema:
            type: string
      requestBody:
        description: Details necessary to update user data
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUser'
      responses:
        '200':
          description: A user model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.
    delete:
      tags:
        - Users
      summary: Delete user
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: deleteUser
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: ID of the user
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Successful deletion of the user
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.


  /sites:
    get:
      tags:
        - Sites
      summary: Get all sites
      operationId: getSites
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
      responses:
        '200':
          description: An array of site models
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Site'
        '403':
          description: Forbidden. User does not have a role that grants read access.
    post:
      tags:
        - Sites
      summary: Create a new site
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: createSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
      requestBody:
        description: Provide the details necessary to create the site
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSite'
      responses:
        '201':
          description: The created site model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.

  /sites/{id}:
    get:
      tags:
        - Sites
      summary: Get a site by ID
      operationId: getSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: A site model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
        '403':
          description: Forbidden. User does not have a role that grants read access.
        '404':
          description: Site not found or does not belong to your organization

  /sites/{id}/properties:
    get:
      tags:
        - Properties
      summary: Get all sub-properties of a site
      operationId: getPropertiesOnSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: An array of property models
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Property'
        '403':
          description: Forbidden. User does not have a role that grants read access.
        '404':
          description: Site not found or does not belong to your organization
    post:
      tags:
        - Properties
      summary: Create a sub-property within a site
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: createPropertyOnSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      requestBody:
        description: Property details. Address is optional and defaults to the parent site's address.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProperty'
      responses:
        '201':
          description: The created property model
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Property'
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.

  /sites/{id}/managers:
    get:
      tags:
        - Sites
      summary: Get managers for a site
      description: Returns all users with SiteManager access to the given site.
      operationId: getManagersBySite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: An array of manager users
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
        '403':
          description: Forbidden. User does not have a role that grants read access.
        '404':
          description: Site not found or does not belong to your organization
    post:
      tags:
        - Sites
      summary: Invite a manager to a site
      description: |-
        If the user already exists, grants SiteManager access immediately and no invitation is
        created (so no mail is sent). Otherwise creates a pending invitation.

        `name` is only required for an email that is not yet a user — it is what the invitation
        is addressed to. For an existing user it is ignored, since no invitation is written.

        Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer.
      operationId: addManagerToSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InviteManager'
      responses:
        '201':
          description: User updated (if they exist) or invitation created (if they do not).
        '400':
          description: |-
            `email` is missing or not a valid address, or `name` was omitted for an email that
            is not yet a user (`code: 5`). Nothing is written when this is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.
        '404':
          description: Site not found or does not belong to your organization

  /sites/{id}/chargers:
    get:
      tags:
        - Sites
      summary: Get all chargers in the specified site
      operationId: getChargersOnSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: An array of charger models
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Charger'
        '403':
          description: Forbidden. User does not have a role that grants read access.
    patch:
      tags:
        - Sites
        - Chargers
      summary: Update the chargers belonging to a provided site
      description: "Requires one of the following roles: ResellerAdmin, DomainManager, SiteManager, Installer."
      operationId: assignChargersToSite
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Site ID
          required: true
          schema:
            type: string
      requestBody:
        description: An array of charger IDs
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignChargersToSite'
      responses:
        '200':
          description: A succesful update.
        '403':
          description: Forbidden. Requires ResellerAdmin, DomainManager, SiteManager, or Installer role.

  /properties/{id}/user-groups:
    get:
      tags:
        - Properties
      summary: Get all user groups associated with a property
      operationId: getUserGroups
      security:
        - BearerAuth: []
      parameters:
        - $ref: '#/components/parameters/B2bId'
        - name: id
          in: path
          description: Property ID
          required: true
          schema:
            type: string
      responses:
        '200':
          description: An array of user group models
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserGroup'
        '403':
          description: Forbidden. User does not have a role that grants read access.

components:
  parameters:
    B2bId:
      name: b2bid
      in: header
      description: >
        BackofficeAdmin only. Specifies the target B2B ID when acting on behalf of a B2B.
        For regular users this header is ignored — the B2B from the JWT is always used.
      required: false
      schema:
        type: string

  schemas:

    SignInResponse:
      type: object
      properties:
        token:
          type: string
          description: Token which should be used as Bearer token in headers to access the API.
        expiresIn:
          type: number
          description: Number of seconds the token will expire in.

    UserCredentials:
      type: object
      properties:
        email:
          type: string
          format: email
        password:
          type: string

    ResetPassword:
      type: object
      properties:
        email:
          type: string
          description: Email address to which reset link will be sent.

    UpdateChargerAccess:
      type: object
      properties:
        usersToAdd:
          type: array
          description: User IDs, to which charger access will be granted.
          items:
            type: string
        usersToRemove:
          type: array
          description: User IDs, from which charger access will be removed.
          items:
            type: string

    Charger:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        authorizedUsers:
          type: array
          items:
            type: string
            description: User IDs which have access to the charger.
        lastSeen:
          type: string
          description: ISO string of the last time this charger has been communicated with.
        address:
          type: string
          description: Address of the site, where the charger is located.
        connectors:
          type: array
          items:
            $ref: '#/components/schemas/Connector'
        chargePoint:
          $ref: '#/components/schemas/ChargePoint'

    Connector:
      type: object
      properties:
        id:
          type: number
        userFacingStatus:
          type: string
          description: available or in-use
        currentUserId:
          type: string
          nullable: true

    ChargePoint:
      type: object
      properties:
        scmSerialNumber:
          type: string
        wbSerialNumber:
          type: string
          nullable: true
        vendor:
          type: string
          nullable: true
        model:
          type: string
          nullable: true

    Session:
      type: object
      properties:
        id:
          type: string
        userId:
          type: string
        chargeAmount:
          type: number
          description: Amount of Watt-hours
        chargerId:
          type: string
          description: Sequential ID number of the charger in the site.
        startDate:
          type: string
          description: ISO string
        endDate:
          type: string
          description: ISO string

    Site:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        address:
          $ref: '#/components/schemas/SiteAddress'

    Property:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        address:
          $ref: '#/components/schemas/SiteAddress'
        createdAt:
          type: string
          description: ISO string

    SiteAddress:
      type: object
      required:
        - houseNumber
        - street
        - city
        - postalCode
        - country
      properties:
        houseNumber:
          type: string
        street:
          type: string
        city:
          type: string
        postalCode:
          type: string
        country:
          type: string

    CreateSite:
      type: object
      required:
        - name
        - address
      properties:
        name:
          type: string
        address:
          $ref: '#/components/schemas/SiteAddress'

    AssignChargersToSite:
      type: object
      properties:
        chargersToAdd:
          type: array
          description: Charger IDs to add to the site.
          items:
            type: string
            maximum: 500
        chargersToRemove:
          type: array
          description: Charger IDs to remove from the site.
          items:
            type: string
            maximum: 500

    CreateUser:
      type: object
      required:
        - metaId
        - email
        - propertyId
      properties:
        metaId:
          type: string
        email:
          type: string
        propertyId:
          type: string
          description: ID of the property (from GET /sites/:id/properties) to assign the user to.

    UpdateUser:
      type: object
      required:
        - metaId
        - email
      properties:
        metaId:
          type: string
        email:
          type: string

    User:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
          format: email
          example: tom@heycharge.com
          description: Email for the user, needs to be unique.
        chargers:
          type: array
          items:
            type: string
          description: A list of charger IDs that the user has access to.
        createdAt:
          type: string
        propertyIds:
          type: array
          items:
            type: string
          description: A list of property IDs the user belongs to.

    InviteManager:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
        name:
          type: string
          description: |-
            Optional, and required only for an email that is not yet a user — otherwise the call
            returns 400 with `code: 5`. For an existing user it is ignored, since access is
            granted without writing an invitation.

    ValidationErrorResponse:
      type: object
      properties:
        message:
          type: string
        code:
          type: integer
          description: |-
            Validation error code. `5` = no name could be resolved for the given email.
            Absent on schema-level failures (missing or malformed `email`), which return the
            standard array-of-constraints message instead.

    CreateProperty:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        address:
          $ref: '#/components/schemas/SiteAddress'
          description: Optional. If omitted, the parent site's address is used.

    UserGroup:
      type: object
      properties:
        id:
          type: string
        propertyId:
          type: string
        name:
          type: string
        color:
          type: string
        userIds:
          type: array
          items:
            type: string
          description: IDs of users belonging to this group.
        chargerIds:
          type: array
          items:
            type: string
          description: IDs of chargers assigned to this group.
        planId:
          type: string
          nullable: true

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

    ApiKeyAuth:
      type: apiKey
      name: x-heycharge-api-key
      in: header
