Gravity Connect API (Virtual Peaker)

The VPP half of the Gravity Connect specification — the publishing endpoints Virtual Peaker hosts so an integrated device partner can stream device signals, settings, command status, and enrollment events back into the platform. Version 2.0.6, OpenAPI 3.0.0, 5 paths / 5 operations, all under the Publishing tag and all keyed on a per-program PROGRAM_PUBLISH_KEY. Base URL https://partner.virtualpeaker.io/v1, confirmed live (AWS API Gateway, anonymous request returns HTTP 403 MissingAuthenticationTokenException). Authentication is not OAuth on this side: requests are signed with an HMAC-SHA256 of the request body using a PROGRAM_PUBLISH_SECRET or DEVICE_PUBLISH_SECRET and sent as `Authorization: Publish `.

OpenAPI Specification

virtual-peaker-gravity-connect-vpp-api-openapi.yml Raw ↑
openapi: 3.0.0
info:
  description: "# Introduction\nThis is the documentation for the endpoints implemented by Virtual Power Plants (VPPs) for integrating devices with the Gravity Connect API.\n\nThe endpoints that a Device Partner implements are described in the [Device Partner Guide](./device-partner-api.html).\n\n# Changelog\nBelow are any noteworthy changes to the API spec.\n\n* 2.0.6 - Revised the explanatory sections of the documentation.\n* 2.0.5 - Added Device Partner Driven Enrollment.\n* 1.2.2 - Added maximum payload size\n* 1.2.1 - Clarifying command status vs device command status reporting\n* 1.2.0 - Clarifying that auth header is Authorization not Authentication\n* 1.1.0 - Adding pairingCode as an optional field for `Publish device enrollment`\n* 1.0.1 - Fixing the `device` key in the `Publish device enrollment status` to refer to the device details rather than just the kind of device\n\n# Authentication\nGravity Connect uses the following authentication models:\n\n**Requests from the VPP to the Device Partner's endpoints**\n* These requests use OAuth 2.0 authentication. Please note that these OAuth credentials are required even if the homeowner enrollment flow is not OAuth-based. If the homeowner enrollment flow is OAuth-based, it can use separate credentials or it can re-use these.\n* The Device Partner provides:\n  * `clientId`: A program-specific ID.\n  * `clientSecret`: A program-specific secret.\n\n**Requests from the Device Partner to the VPP's endpoints**\n* These requests authenticate using a combination of the following credentials and HMAC authentication. The signature validation allows Gravity Connect endpoints to be called only by authorized parties in possession of the valid `PUBLISH_SECRET`.\n  * The VPP provides:\n    * `PROGRAM_PUBLISH_KEY`: The unique identifier for a utility program, provided when a program is set up.\n    * `PROGRAM_PUBLISH_SECRET`: The secret key used to generate HMAC signatures when publishing program-level data, provided when a program is set up.\n    * `DEVICE_PUBLISH_SECRET`: The secret key used to generate HMAC signatures when publishing device-level data, provided when each device is enrolled.\n\nTo authenticate with the VPP's endpoints:\n1. Construct the payload body for the API request.\n2. Generate an HMAC hash on the request body content using the `PROGRAM_PUBLISH_SECRET` or `DEVICE_PUBLISH_SECRET`, as specified for that endpoint.\n3. Set the Authorization header with the signature.\n\nExample: `Authorization: Publish 12345abcdef`\n  * Where 12345abcdef is the HMAC hash.\n  * See the HMAC generation script below for sample code.\n\n## HMAC Script\nThe below script is an example of how to generate an HMAC for some payload in javascript. Other languages should have standard crypto libraries that work similarly.\n\n```javascript\nconst crypto = require('crypto');\n\nconst secret = 'your_secret_here';\nconst body = `{\n  \"uid\": \"your_device_uid\",\n  \"kind\": \"TSTAT\",\n  \"signal\": [{\n    \"key\": \"mode\",\n    \"value\": \"HEAT\",\n    \"time\": \"2021-01-28T15:36:48.586697\"\n  }],\n  \"setting\": [{\n    \"key\": \"max-setpoint\",\n    \"value\": 31.0,\n    \"time\": \"2020-10-20T22:10:57.101616\"\n  }]\n}`;\n\nconst computedHMAC = crypto.createHmac('sha256', secret).update(body).digest('hex');\nconsole.log(`Publish ${computedHMAC}`);\n```\n\n# Error Handling\nFor the following error codes, please retry using exponential backoff:\n* 429\n* 502\n* 503\n* 504\n\nAll other requests that receive an error should be discarded."
  x-logo:
    url: ./assets/vp_logo.png
    backgroundColor: '#FFFFFF'
    altText: Virtual Peaker Logo
  version: 2.0.6
  title: Gravity Connect API (Virtual Peaker)
  license:
    name: BSD
servers:
- url: https://partner.virtualpeaker.io/v1
tags:
- name: Publishing
  description: 'All of the endpoints below allow the Device Partner to publish data to the Virtual Peaker platform, which avoids Virtual Peaker having to constantly poll the data when there haven''t been any changes. Part of the [Device Partner Implementation](example.com) does include endpoint to read poll for data, which could be used by Virtual Peaker to gather the first batch of data or for debugging.


    **Notes:**

    - The maximum supported payload size for all requests listed below is 262,144 bytes.

    '
paths:
  /publish/{PROGRAM_PUBLISH_KEY}/update:
    post:
      summary: Publish signal/setting value
      description: This endpoint publishes device data to Virtual Peaker. The signal and setting types vary by device type. Please reference the [Device Specific Definitions section](device-partner-api.html#section/Device-Specific-Definitions) to see the signals and settings for each device. Also, a required field is required overall for that device although not required every time data is reported for that device.
      operationId: publishSignalSetting
      tags:
      - Publishing
      parameters:
      - $ref: '#/components/parameters/DeviceHMAC'
      - $ref: '#/components/parameters/programPublishKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - uid
              - kind
              - signal
              - setting
              properties:
                uid:
                  type: string
                  description: The partner's unique device identifier
                kind:
                  $ref: '#/components/schemas/DeviceKindEnum'
                signal:
                  type: array
                  items:
                    $ref: '#/components/schemas/SignalSetting'
                setting:
                  type: array
                  items:
                    $ref: '#/components/schemas/SignalSetting'
      responses:
        '202':
          $ref: '#/components/responses/accepted'
        '400':
          $ref: '#/components/responses/badRequest'
        '401':
          $ref: '#/components/responses/unauthorized'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Details'
  /publish/{PROGRAM_PUBLISH_KEY}/command:
    post:
      summary: Publish command status
      description: Used to report back the status of a command. For more details please reference the FAQs
      operationId: publishCommand
      tags:
      - Publishing
      parameters:
      - $ref: '#/components/parameters/HMAC'
      - $ref: '#/components/parameters/programPublishKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/CommandState'
              - required:
                - refId
      responses:
        '202':
          $ref: '#/components/responses/accepted'
        '400':
          $ref: '#/components/responses/badRequest'
        '401':
          $ref: '#/components/responses/unauthorized'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Details'
  /publish/{PROGRAM_PUBLISH_KEY}/command/device:
    post:
      summary: Publish device command status
      description: Only used if grouping is being used. For more details please reference the FAQs
      operationId: publishDeviceCommand
      tags:
      - Publishing
      parameters:
      - $ref: '#/components/parameters/HMAC'
      - $ref: '#/components/parameters/programPublishKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/CommandState'
              - required:
                - refId
                - uid
                properties:
                  uid:
                    type: string
                    description: DEVICE UID
      responses:
        '202':
          $ref: '#/components/responses/accepted'
        '400':
          $ref: '#/components/responses/badRequest'
        '401':
          $ref: '#/components/responses/unauthorized'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Details'
  /publish/{PROGRAM_PUBLISH_KEY}/device:
    post:
      summary: Publish device enrollment status
      description: If using OAuth Device Onboarding, devices are assumed to be enrolled when initially discovered.
      operationId: publishDeviceEnrollment
      tags:
      - Publishing
      parameters:
      - $ref: '#/components/parameters/HMAC'
      - $ref: '#/components/parameters/programPublishKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - state
              - device
              - time
              properties:
                state:
                  type: string
                  enum:
                  - enrolled
                  - unenrolled
                device:
                  $ref: '#/components/schemas/DeviceDetails'
                time:
                  type: string
                  format: date-time
                pairingCode:
                  type: string
                  description: "Only passed if using Pairing Code Device Discovery. \nThe current format is:\n* 2 alphanumeric characters to denote the pairing code prefix representing the program\n* 5 random numeric characters that VP uses to link the user to an existing device\n* 1 check digit. This check digit will be generated following the Luhn algorithm to ensure the 5 digit number is valid. This validation can be performed on the device partner side to provide immediate feedback, but will also be done within our API."
      responses:
        '202':
          $ref: '#/components/responses/accepted'
        '400':
          $ref: '#/components/responses/badRequest'
        '401':
          $ref: '#/components/responses/unauthorized'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Details'
  /publish/{PROGRAM_PUBLISH_KEY}/enrollment:
    post:
      summary: Publish device partner driven enrollment
      description: Used for publishing device and site information for in-app enrollment, pre-enrollment from OEM-owned marketplaces, and bulk device enrollment.
      operationId: publishDevicePartnerDrivenEnrollment
      tags:
      - Publishing
      parameters:
      - $ref: '#/components/parameters/HMAC'
      - $ref: '#/components/parameters/programPublishKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - devices
              - time
              - site
              properties:
                time:
                  type: string
                  format: date-time
                  description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset.
                devices:
                  type: array
                  items:
                    $ref: '#/components/schemas/DeviceDetails'
                site:
                  $ref: '#/components/schemas/UserDetails'
      responses:
        '202':
          $ref: '#/components/responses/accepted'
        '400':
          $ref: '#/components/responses/badRequest'
        '401':
          $ref: '#/components/responses/unauthorized'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Details'
components:
  requestBodies:
    manageGroup:
      required: true
      content:
        application/json:
          schema:
            type: object
            required:
            - deviceUids
            - action
            properties:
              deviceUids:
                type: array
                description: Array of device uids for the Device Partner to assign to or remove from the group
                items:
                  type: string
              action:
                type: string
                enum:
                - add
                - remove
  parameters:
    programPublishKey:
      name: PROGRAM_PUBLISH_KEY
      in: path
      required: true
      description: The identifier of the program within Virtual Peaker
      schema:
        type: string
    DeviceHMAC:
      in: header
      name: Authorization
      description: '`Publish`, followed by the value of: HMAC(algorithm=`sha256``, key=DEVICE_PUBLISH_SECRET, data=BODY)'
      schema:
        type: string
        pattern: ^Publish [a-f\d]+$
      required: true
    HMAC:
      in: header
      name: Authorization
      description: '`Publish`, followed by the value of: HMAC(algorithm=`sha256``, key=PROGRAM_PUBLISH_SECRET, data=BODY)'
      schema:
        type: string
        pattern: ^Publish [a-f\d]+$
      required: true
  schemas:
    DeviceKindEnum:
      type: string
      enum:
      - HWH
      - TSTAT
      - BATTERY
      - EVSE
      - V2G
      - STORAGE-HVAC
    DeviceDetails:
      type: object
      required:
      - uid
      - kind
      - type
      properties:
        uid:
          type: string
          description: DEVICE_UID, unique within the partner
        kind:
          $ref: '#/components/schemas/DeviceKindEnum'
        name:
          type: string
        type:
          type: string
          description: model name/number
        serialNumber:
          type: string
    CommandState:
      type: object
      required:
      - state
      - time
      properties:
        state:
          type: string
          description: 'State of the command:

            * PENDING - scheduled but not started

            * IN_PROGRESS - when pending command hits start time

            * FAILED - something has prevented the command from being scheduled or started

            * OPT_OUT - device owner has specified that they don’t want to participate in an event

            * CANCELLED - utility has said they don’t want the event to happen

            * COMPLETED - end time of event has been reached without becoming CANCELLED or OPT_OUT'
          enum:
          - PENDING
          - IN_PROGRESS
          - FAILED
          - OPT_OUT
          - CANCELLED
          - COMPLETED
        description:
          type: string
          description: human readable status
        time:
          type: string
          format: date-time
          description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset.
        refId:
          type: string
          description: Command Reference ID
    SignalSetting:
      type: object
      required:
      - key
      - value
      - time
      properties:
        key:
          type: string
        value:
          oneOf:
          - type: string
          - type: number
        time:
          type: string
          format: date-time
          description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset.
    Details:
      type: object
      required:
      - message
      properties:
        message:
          type: string
          description: A human readable response. Because there's no standard for what is included or how information should be formatted, this should not be parsed and utilized programmatically.
    ServiceAddress:
      type: object
      description: Service address associated with the user account. Optional, but strongly recommended.
      properties:
        streetAddress:
          type: string
        streetAddress2:
          type: string
        city:
          type: string
        state:
          type: string
          description: Within the US, passed as a 2 letter abbreviation
        postalCode:
          type: string
        country:
          type: string
          description: A 2 letter indication of country. Defaults to 'US'. Following [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
    UserDetails:
      type: object
      required:
      - email
      properties:
        userId:
          type: string
          description: Unique identifier for the user within the device partner's platform
        email:
          type: string
        accountNumber:
          type: string
          description: Utility customer identifier (if available)
        firstName:
          type: string
        lastName:
          type: string
        serviceAddress:
          description: Optional but strongly recommended
          $ref: '#/components/schemas/ServiceAddress'
  responses:
    accepted:
      description: Message has been accepted for processing
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Details'
    badRequest:
      description: Request was not properly formatted
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Details'
    unauthorized:
      description: HMAC validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Details'