Voltus API Reference
Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
Welcome to the Voltus API Reference, where you can learn all about Voltus' API. For tutorials and concept-level documentation, see our Documentation Portal.
The live production API hostname is api.voltus.co
.
Introduction to the Voltus platform
Voltus gives partners a seamless way to monetize their DER assets in wholesale energy markets, and in some cases, directly with utilities.
Voltus helps you monetize your assets end-to-end:
- Register your assets: Tell us which DERs you want to monetize, and we'll register them with grid operators and utilities on your behalf.
- Share your availability and capacity: Let us know when and how much flexible energy capacity you want to sell into the market.
- Monitor your consumption: Give us real-time visibility into your energy usage, so we can calculate your performance, and you can get paid. We can provide hardware solutions if necessary.
- Get dispatched: Our market-integrated platform communicates demand response events and pricing signals to you and your DERs.
- Get paid: We handle settlement with the markets or utilities and send cash your way. We also provide you with detailed performance and financial reporting.
You will work with a dedicated account manager to support integration activities. Currently, the Voltus API supports steps 3 and 4. Future versions of the API will support this flow end-to-end.
In addition to our REST API, we also offer OpenADR capabilities (OpenADR 2.0a), the industry standard for Automated Demand Response. Read more about our implementation at the end of the documentation.
Versioning
We use date-based versioning. api.voltus.co
will always redirect to the latest version.
Current version: api.voltus.co/2022-04-15
Changelog
- Renamed "Facilities" to "Sites - Entity IDs are now strings instead of ints - New Authorization header `X-Voltus-API-Key` - Removed support for `Authorization: Bearer` headerPrevious versions:
- api.voltus.co/2020-12-30
Authentication
- API Key (api_key_header_X-Voltus-API-Key)
- Parameter Name: X-Voltus-API-Key, in: header. The API uses API key authentication. API keys are provided by your account manager. Each request must include an
X-Voltus-API-Key
header where the value is your API key. Code examples for each endpoint will demonstrate how this header can be added in various languages.
- Parameter Name: X-Voltus-API-Key, in: header. The API uses API key authentication. API keys are provided by your account manager. Each request must include an
Errors
Example error responses
{
"message": "A create webhook request must contain an 'events' field",
"type": "Bad Request"
}
{
"message": "Permission denied",
"type": "Unauthorized"
}
Voltus uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx
range indicate success. Codes in the 4xx
range indicate an error that failed given the information provided (e.g., a required parameter was omitted, an api key has been revoked, etc.). Codes in the 5xx
range indicate an error with Voltus's servers (these are rare).
Attributes
message
string
A human-readable message providing more details about the error.
type
string
The error type. These types will always match the name of the name of the http status code that is used in the response. One of Unauthorized
, Bad Request
, Internal Server Error
, Too Many Requests
or Not Found
.
Dispatches
A Dispatch
is a discrete period of time that Voltus, a grid operator, or a utility calls upon a participant to reduce electricity consumption.
Along with dispatches related to regular program events, Voltus will also send test events to validate that a customer can receive notifications, and that they can curtail their load. These types of test events are coordinated in advance.
Dispatch notification timing varies depending on the programs you participate in. Some are sent as far as 36hrs in advance, while fast response programs will dispatch with only 10 minutes of notice before the resource must be fully curtailed. Make sure to work with your Voltus account manager to better understand the specific dispatch timing for the programs that you participate in.
Dispatch events always specify a start_time
. On rare occasions, the start_time
may change.
The end_time
field is omitted until the end of the event is announced by the grid operator. The end_time
may also change.
You can test your integration with the Create a test dispatch
endpoint, which will create dispatches with test = True
List recent dispatches.
Code samples
curl -X GET https://api.voltus.co/2022-04-15/dispatches \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.get('https://api.voltus.co/2022-04-15/dispatches', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/dispatches");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const headers = {
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/dispatches',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("GET", "https://api.voltus.co/2022-04-15/dispatches", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
GET /2022-04-15/dispatches
Returns a list of dispatches.
By default, the list dispatches endpoint returns all dispatch events from the previous 24 hours. This endpoint is commonly used to implement a polling pattern to listen for new dispatch notifications.
If you would like to implement a polling pattern to check for new dispatches, we recommend the following approach:
- Make requests to this endpoint every 30 seconds in a loop. You may make requests more quickly if you'd like, but if you need very fast response times, we recommend using Webhooks instead. Excessive polling requests made to this endpoint maybe result in a
429 - Too Many Requests
response. - When you make a request, check for new dispatches in the response body. Each dispatch has a unique
id
attribute that can be used for identification. - Check each new dispatch's
start_time
, list ofsites
, andtest
value to see if, when, and how you need to respond. - If you need to respond, begin curtailment for the affected sites.
- On subsequent requests, make sure you're checking for any updates that have been made to the active dispatch.
end_time
could be populated or be updated, and in extremely rare cases, the dispatch may be cancelled. If the dispatch is cancelled, theauthorized
attribute will be set tofalse
. - Once the dispatch
end_time
has passed, curtailment can stop as long as there are no other dispatches that require continued curtailment.
NOTES:
- Sites can be involved in multiple overlapping dispatches at the same time. Do not stop curtailment for a given site until all the active dispatches for that site have reached their end time (or the dispatch(es) have been canceled). If two events are scheduled at the same time and one is shorter than the other, the site must continue to curtail for the full duration of the second event, e.g. for Dispatch A from 9:00-10:00 and Dispatch B from 9:00-11:00, site must curtail from 9:00-11:00.
- Dispatches can be scheduled back-to-back, e.g. Dispatch A from 9:00-10:00 and Dispatch B from 10:00-11:00. If your system will be pre-curtailing (e.g. starting the load drop 5 min before start time to ensure performance), make sure that your system does not ignore the second dispatch, and continues to curtail through the end of the second event.
- A single dispatch can contain multiple sites. All sites listed must curtail during the dispatch time period.
- All dispatches will be returned in a single request. While the fields
page
andper_page
have been reserved for future use, their values should be ignored.
Parameters
Example responses
200 Response
{
"dispatches": [
{
"authorized": true,
"end_time": "2024-02-02T10:00:00Z",
"id": "5j94",
"program": {
"market": "CAISO",
"name": "CAISO Operating Reserves",
"program_type": "ancillary_services",
"timezone": "US/Pacific"
},
"sites": [
{
"commitment": 1248,
"customer_location_id": "site102",
"drop_by": 1248,
"id": "yd4g",
"name": "The coolest site"
},
{
"commitment": 5.3,
"customer_location_id": "",
"drop_by": 5.3,
"id": "40wl",
"name": "Just an ok site"
}
],
"start_time": "2024-02-02T09:00:00Z",
"test": false
},
{
"authorized": true,
"end_time": "2024-02-02T11:00:00Z",
"id": "zg1p",
"program": {
"market": "ERCOT",
"name": "ERCOT LR",
"program_type": "ancillary_services",
"timezone": "US/Central"
},
"sites": [
{
"commitment": null,
"customer_location_id": "site101",
"drop_by": null,
"id": "xv1w4",
"name": "A cool site"
},
{
"commitment": null,
"customer_location_id": "site102",
"drop_by": null,
"id": "yd4g",
"name": "The coolest site"
}
],
"start_time": "2024-02-02T10:00:00Z",
"test": false
},
{
"authorized": true,
"id": "zg1p",
"program": {
"market": "CAISO",
"name": "CAISO Operating Reserves",
"program_type": "ancillary_services",
"timezone": "US/Pacific"
},
"sites": [
{
"commitment": null,
"customer_location_id": "site101",
"drop_by": null,
"id": "xv1w4",
"name": "A cool site"
},
{
"commitment": null,
"customer_location_id": "site102",
"drop_by": null,
"id": "yd4g",
"name": "The coolest site"
}
],
"start_time": "2024-02-02T10:00:00Z",
"test": false
},
{
"authorized": true,
"end_time": "2024-01-15T22:00:00Z",
"id": "ez4p",
"program": {
"market": "",
"name": "Voltus Communications Test",
"program_type": "capacity",
"timezone": "US/Eastern"
},
"sites": [
{
"commitment": 0,
"customer_location_id": "site101",
"drop_by": 0,
"id": "xv1w4",
"name": "A cool site"
},
{
"commitment": 0,
"customer_location_id": "site102",
"drop_by": 0,
"id": "yd4g",
"name": "The coolest site"
},
{
"commitment": 0,
"customer_location_id": "",
"drop_by": 0,
"id": "40wl",
"name": "Just an ok site"
},
{
"commitment": 0,
"customer_location_id": "",
"drop_by": 0,
"id": "6rnp",
"name": "A decent site"
}
],
"start_time": "2024-01-15T21:00:00Z",
"test": true
}
],
"page": 0,
"perPage": 0
}
Response Attributes
dispatches
array
authorized
boolean
The authorization status of the dispatch. If this value is false, a dispatch is cancelled, and curtailment can stop immediately.
end_time
string
End time of the dispatch. This attribute will be omitted until an end time is determined. All dispatches will eventually have an end time unless they are cancelled. Formatted as a RFC 3339 timestamp.
id
string
Primary key of the dispatch.
program
object
market
string
The market the program is participating in
name
string
The program's name
program_type
string enum
the type of program
-
valid_values:
[ancillary_services
,capacity
,energy
,peak_saver
,utility_interruptible_rate
,utility_program
,not_specified
, ]
timezone
string
The program's time zone (ignore to determine dispatch start and end times)
sites
array
The sites that must curtail as a result of this dispatch
commitment
number
Expected electricity reduction in kW when measured against the site's registered baseline for this dispatch.
customer_location_id
string
An identifier for this site provided by a customer. This can be any identifier supplied by the site owner, such as a store number.
DEPRECATED
drop_by
number
Amount of power to reduce during dispatch (kW). Nullable. Deprecated.
id
string
Primary key of the site
name
string
Name of the site
start_time
string
Start time of the dispatch. Formatted as a RFC 3339 timestamp.
test
boolean
Whether the dispatch is a test. If this value is false, this is real, non-test dispatch, and you should curtail the site(s). If this value is true, this is a test dispatch, and you do not need to curtail.
page
integer
Page number. Reserved for future use, should ignore for now.
perPage
integer
Number of items per page. Reserved for future use, should ignore for now.
Create a test dispatch
Code samples
curl -X POST https://api.voltus.co/2022-04-15/dispatches \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.post('https://api.voltus.co/2022-04-15/dispatches', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/dispatches");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const inputBody = '{
"end_time": "2024-12-22T06:04:02Z",
"start_time": "2024-12-22T05:04:02Z"
}';
const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/dispatches',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("POST", "https://api.voltus.co/2022-04-15/dispatches", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
POST /2022-04-15/dispatches
Creates a test dispatch.
Create a new dispatch object. Upon sending a successful request, a new dispatch object will be persisted. The new dispatch will be returned in the response to GET /dispatches
for the appropriate time-frame, and will also be available at its ID-specific path. If you have a webhook registered for the dispatch.create
event type, it will be triggered. This type of dispatch will not trigger any customer communications such as email, SMS, or phone calls.
If you plan to use this endpoint, you should use the dispatch.test
field to distinguish between a Voltus-initiated dispatch and a dispatch created through this endpoint. Dispatches created through this endpoint will have test=true
.
Body parameter
{
"end_time": "2024-12-22T06:04:02Z",
"start_time": "2024-12-22T05:04:02Z"
}
Parameters
body
object
end_time
string
End time of the dispatch (RFC 3339). Must be after start_time, within the next 72 hours.
start_time
string
Start time of the dispatch (RFC 3339). Must be in the future, within the next 72 hours.
Example responses
Retrieve a dispatch
Code samples
curl -X GET https://api.voltus.co/2022-04-15/dispatches/{id} \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.get('https://api.voltus.co/2022-04-15/dispatches/{id}', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/dispatches/{id}");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const headers = {
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/dispatches/{id}',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("GET", "https://api.voltus.co/2022-04-15/dispatches/{id}", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
GET /2022-04-15/dispatches/{id}
Retrieves the dispatch with the given ID.
Can be used to explicitly check for the status of an individual dispatch.
Parameters
id
string
The dispatch ID
Example responses
200 Response
{
"authorized": true,
"end_time": "2024-02-02T10:00:00Z",
"id": "5j94",
"program": {
"market": "CAISO",
"name": "CAISO Operating Reserves",
"program_type": "ancillary_services",
"timezone": "US/Pacific"
},
"sites": [
{
"commitment": 1248,
"customer_location_id": "site102",
"drop_by": 1248,
"id": "yd4g",
"name": "The coolest site"
},
{
"commitment": 5.3,
"customer_location_id": "",
"drop_by": 5.3,
"id": "40wl",
"name": "Just an ok site"
}
],
"start_time": "2024-02-02T09:00:00Z",
"test": false
}
Response Attributes
authorized
boolean
The authorization status of the dispatch. If this value is false, a dispatch is cancelled, and curtailment can stop immediately.
end_time
string
End time of the dispatch. This attribute will be omitted until an end time is determined. All dispatches will eventually have an end time unless they are cancelled. Formatted as a RFC 3339 timestamp.
id
string
Primary key of the dispatch.
program
object
market
string
The market the program is participating in
name
string
The program's name
program_type
string enum
the type of program
-
valid_values:
[ancillary_services
,capacity
,energy
,peak_saver
,utility_interruptible_rate
,utility_program
,not_specified
, ]
timezone
string
The program's time zone (ignore to determine dispatch start and end times)
sites
array
The sites that must curtail as a result of this dispatch
commitment
number
Expected electricity reduction in kW when measured against the site's registered baseline for this dispatch.
customer_location_id
string
An identifier for this site provided by a customer. This can be any identifier supplied by the site owner, such as a store number.
DEPRECATED
drop_by
number
Amount of power to reduce during dispatch (kW). Nullable. Deprecated.
id
string
Primary key of the site
name
string
Name of the site
start_time
string
Start time of the dispatch. Formatted as a RFC 3339 timestamp.
test
boolean
Whether the dispatch is a test. If this value is false, this is real, non-test dispatch, and you should curtail the site(s). If this value is true, this is a test dispatch, and you do not need to curtail.
Schedule
Make changes to a site's schedule
Code samples
curl -X POST https://api.voltus.co/2022-04-15/schedule/sites/{site_id} \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.post('https://api.voltus.co/2022-04-15/schedule/sites/{site_id}', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/schedule/sites/{site_id}");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const inputBody = '{
"available": false,
"end_time": "2024-12-22T16:00:00Z",
"start_time": "2024-12-22T14:00:00Z"
}';
const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/schedule/sites/{site_id}',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("POST", "https://api.voltus.co/2022-04-15/schedule/sites/{site_id}", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
POST /2022-04-15/schedule/sites/{site_id}
Changes the availability of a site.
On a successful response (status code 200), the site’s availability has been updated. If availability was turned off, the site will no longer be dispatched during the requested time period. turning on availability is currently not supported.
A non-200 status code response indicates the site's availability has not been updated. Outside of authentication issues and unexpected technical failures, this could happen if it is too late to propagate the requested schedule change to market. Cutoff times vary from market to market. Request failure means that the site may still be dispatched.
Body parameter
{
"available": false,
"end_time": "2024-12-22T16:00:00Z",
"start_time": "2024-12-22T14:00:00Z"
}
Parameters
site_id
string
The id of the site.
body
object
available
boolean
Whether or not the site is dispatchable during the given time period period. Setting availability to true is currently not supported.
end_time
string
An RFC 3339 date-time string (with time-offsets) indicating the start time of availability block.
start_time
string
An RFC 3339 date-time string (with time-offsets) indicating the start time of availability block.
Example responses
Sites
A site represents a single physical location that can be curtailed in response to a demand response event.
List all sites
Code samples
curl -X GET https://api.voltus.co/2022-04-15/sites \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.get('https://api.voltus.co/2022-04-15/sites', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/sites");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const headers = {
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/sites',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("GET", "https://api.voltus.co/2022-04-15/sites", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
GET /2022-04-15/sites
Returns a list of your sites.
The sites endpoint is important because a given API key will only get dispatch notifications that include sites visible to the account. You should make sure that all expected sites are returned by this endpoint.
Sites include id
and customer_location_id
fields that can be used to identify sites in a dispatch communication. A site's name
value may change, and should not be used as a static identifier.
All sites will be returned in a single request. The values of page
and per_page
have been reserved for future use, but should be ignored at present.
Parameters
Example responses
200 Response
{
"page": 0,
"perPage": 0,
"sites": [
{
"customer_location_id": "site101",
"id": "xv1w4",
"meters": [
{
"id": "wpv31",
"name": "Primary Meter"
},
{
"id": "yvve",
"name": "Generator"
}
],
"name": "A cool site"
},
{
"customer_location_id": "site102",
"id": "yd4g",
"meters": [
{
"id": "njj1",
"name": "The best meter"
}
],
"name": "The coolest site"
},
{
"customer_location_id": "",
"id": "40wl",
"meters": [],
"name": "Just an ok site"
},
{
"customer_location_id": "",
"id": "6rnp",
"meters": [
{
"id": "2j3y",
"name": "The sites meter name"
}
],
"name": "A decent site"
}
]
}
Response Attributes
page
integer
Page number. Reserved for future use, should ignore for now.
perPage
integer
Number of items per page. Reserved for future use, should ignore for now.
sites
array
customer_location_id
string
An identifier for this site provided by a customer. This can be any identifier supplied by the site owner, such as a store number.
id
string
Primary key of the site
meters
array
Meters associated with this site
id
string
Primary key of the meter
name
string
Name of the meter
name
string
Name of the site
Telemetry
Telemetry is a critical component of energy market participation. Voltus, grid operators, and utilities use telemetry data to verify that a DER is delivering expected performance.
Today, these telemetry endpoints only deal with power consumption. kW
or kWh
values can be posted along with interval length to communicate the rate that energy is consumed over a period of time. Other telemetry information, like power factor, state of charge, and power consumption with flow direction will be added at a later date. If providing or consuming this information is important to you, please reach out to your account manager.
We interpret the kW
value as the average instantaneous power reading for the given interval. For example, posting a continuous series of 120 readings of 30 seconds, 10 kW
each will translate to 10 kWh summed power consumption.
kWh
on the other hand, measures the amount of energy consumed within the given interval.
Positive values represent the reduction in energy consumption. Negative values represent the injection of energy into the grid. Please contact your customer representative if you plan on sending negative values.
Upload telemetry data
Code samples
curl -X POST https://api.voltus.co/2022-04-15/telemetry \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.post('https://api.voltus.co/2022-04-15/telemetry', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/telemetry");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const inputBody = '{
"telemetry": [
{
"interval_seconds": 30,
"meter_id": "wpv31",
"timestamp": "2024-01-02T09:00:00Z",
"units": "kW",
"value": 0.2
},
{
"interval_seconds": 30,
"meter_id": "wpv31",
"timestamp": "2024-01-02T09:00:30Z",
"units": "kW",
"value": 0.25
},
{
"interval_seconds": 60,
"site_id": "yd4g",
"timestamp": "2024-01-02T09:00:00Z",
"units": "kW",
"value": 0.5
}
]
}';
const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/telemetry',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("POST", "https://api.voltus.co/2022-04-15/telemetry", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
POST /2022-04-15/telemetry
This endpoint is intended for bulk-data submission and can receive many telemetry points in the same request. You can also post non-contiguous intervals, intervals for multiple meters, and intervals that have been submitted before, all without issue. We'll handle the de-duplication of double-submitted points, and you can take this into consideration when implementing retry and failure logic.
Body parameter
{
"telemetry": [
{
"interval_seconds": 30,
"meter_id": "wpv31",
"timestamp": "2024-01-02T09:00:00Z",
"units": "kW",
"value": 0.2
},
{
"interval_seconds": 30,
"meter_id": "wpv31",
"timestamp": "2024-01-02T09:00:30Z",
"units": "kW",
"value": 0.25
},
{
"interval_seconds": 60,
"site_id": "yd4g",
"timestamp": "2024-01-02T09:00:00Z",
"units": "kW",
"value": 0.5
}
]
}
Parameters
body
object
telemetry
array
interval_seconds
integer
Number of seconds in this interval. This must be divisible by 30 and the maximum value is 300 - requests where any interval violates these rules will fail completely and will need to be corrected and resubmitted. E.g. If you are posting the total `kWh` consumed in 5 minutes of data this value will be 300.
kW
number
DEPRECATED. Please use 'value' and 'units' instead. Average instantaneous power reading for this interval
meter_id
string
ID of the meter that you're writing telemetry for. Cannot be used with site_id. Can be found with the /sites endpoint.
site_id
string
ID of the site that you're writing telemetry for. The site must only have one meter. Cannot be used with meter_id. Can be found with the /sites endpoint.
timestamp
string
The timestamp of the END of this telemetry interval in RFC3339 format. E.g. If the timestamp is 10:30 and the interval_seconds is 60 seconds, this interval is for 10:29-10:30.
units
string enum
The units of the measurement. Currently support kW and kWh. Must be ommitted if using deprecated field 'KW'. One request must use the same units for all intervals.
-
valid_values:
[kW
,kWh
, ]
value
number
The value of the telemetry measurement for this interval. A positive value represents load. For meters capable of export to the grid (solar net metering, for example), exported energy is represented by a negative value. Must be omitted if using deprecated field `kW`
Example responses
Retrieve telemetry data in kW
Code samples
curl -X GET https://api.voltus.co/2022-04-15/telemetry/kw?start_time=2020-08-22T05%3A00%3A30Z&site_id=xv1w4,yd4g \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.get('https://api.voltus.co/2022-04-15/telemetry/kw', params={
'start_time': '2020-08-22T05:00:30Z', 'site_id': [
"xv1w4",
"yd4g"
]
}, headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/telemetry/kw?start_time=2020-08-22T05%3A00%3A30Z&site_id=xv1w4,yd4g");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const headers = {
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/telemetry/kw?start_time=2020-08-22T05%3A00%3A30Z&site_id=xv1w4,yd4g',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("GET", "https://api.voltus.co/2022-04-15/telemetry/kw", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
GET /2022-04-15/telemetry/kw
THIS ENDPOINT IS IN BETA AND SUBJECT TO CHANGE. returns a list of telemetry readings for one or more sites. The maximum number of sites per request is 10. The maximum request interval is 48 hours. This is intended for retrieving historical data. Requesting data less than 10 minutes old may not return all readings that will eventually be available.
Parameters
start_time
string
An RFC3339 compatible string that denotes the beginning (exclusive) of the requested interval. Must be aligned to 30 seconds
end_time
string
An RFC3339 compatible string that denotes the end (inclusive) of the requested interval. Defaults to 24 hours ahead of start_time if omitted. Must be aligned to 30 seconds.
interval_seconds
integer
Interval width desired, in seconds. Defaults to 30. Use one of 30, 60, 300, 900, 1800, 3600, 21600.
site_id
array
ID of a site. For multiple sites, add multiple instances of this parameter.
Enumerated Values
Parameter | Value |
---|---|
interval_seconds | 30 |
interval_seconds | 60 |
interval_seconds | 300 |
interval_seconds | 900 |
interval_seconds | 1800 |
interval_seconds | 3600 |
interval_seconds | 21600 |
Example responses
200 Response
{
"data": {
"sites": [
{
"meters": [
{
"meter_id": "wpv31",
"telemetry": [
{
"interval_seconds": 30,
"timestamp": "2024-01-02T09:00:00Z",
"units": "kW",
"value": 0.2
}
]
},
{
"meter_id": "wpv31",
"telemetry": [
{
"interval_seconds": 30,
"timestamp": "2024-01-02T09:00:30Z",
"units": "kW",
"value": 0.25
}
]
}
],
"site_id": "xv1w4"
},
{
"meters": [
{
"meter_id": "njj1",
"telemetry": [
{
"interval_seconds": 60,
"timestamp": "2024-01-02T09:00:00Z",
"units": "kW",
"value": 0.5
}
]
}
],
"site_id": "yd4g"
}
]
}
}
Response Attributes
data
object
sites
array
Array of readings for requested sites.
meters
array
Array of meters with list of readings in each.
meter_id
string
ID of the meter
telemetry
array
A list of readings.
interval_seconds
integer
Number of seconds in this interval.
timestamp
string
The timestamp of the END of this telemetry interval in RFC3339 format. E.g. If the timestamp is 10:30:00, this interval is for 10:29:30-10:30:00.
units
string enum
The units of the measurement. Currently supports kw.
-
valid_values:
[kW
, ]
value
number
The value of the telemetry measurement for this interval. A positive value represents load. For meters capable of export to the grid (solar net metering, for example), exported energy is represented by a negative value.
site_id
string
ID of the site
Webhooks
Use incoming webhooks to get real-time updates.
Voltus uses webhooks to notify your application when a new event happens affecting your account. A webhook enables Voltus to push real-time notifications to your app. When an event happens, we send a JSON payload over HTTPS. You can then use these notifications to execute actions in your backend systems. We currently support the following event types:
Event Name | Description |
---|---|
dispatch.create | A new dispatch is created |
dispatch.update | An existing dispatch is modified. Most commonly, an end time is provided. |
Steps to Configuring A Webhook
- Identify the events you want to monitor. Usually, this will be both of the above events.
- Create a webhook endpoint as an HTTP endpoint (URL) on your backend system.
- Handle requests from Voltus by parsing the event object and returning a 2xx status code.
- Make a POST /webhooks request to create the new webhook, which will send a test payload to your endpoint.
- Verify that your endpoint is working correctly.
Step 1: Identify the events to monitor
If you are creating a webhook to listen for dispatch events, make sure to create a webhook with {"events": [{"name": "dispatch.update"}, {"name": "dispatch.create"}]}
to ensure that you get dispatch events on new event creation, and when any updates are made.
Step 2: Create a webhook endpoint
Build and deploy a webhook endpoint on your server that can accept webhook requests with a POST method. For example, this route in Flask is a map to a Python webhook function:
@app.route('/voltus_webhooks', methods=['POST'])
def webhook():
voltus_payload = request.json
In this example, the /voltus_webhooks route is configured to accept only POST requests and expects data to be delivered in a JSON payload.
Step 3: Handle requests from Voltus
Your endpoint must be configured to read event objects for the type of event notifications you want to receive. Voltus sends events to your webhook endpoint as part of a POST request with a JSON payload.
Check event objects
Each event is structured as an event object with a name and resource URL. Your endpoint must make a request to the resource URL to obtain the details of the modified resource.
{
"event": {
"name": "dispatch.create"
},
"resource": "/2022-04-15/dispatches/asdf"
}
Return a 2xx response
Your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout. For example, you must return a 2xx response before receiving telemetry from curtailing sites. Valid status codes are 200
, 201
, 202
, and 204
. Voltus also follows redirects if the endpoint responds with status 301
or 302
and a valid Location
header.
Built-in retries
For all other response status codes, Voltus will retry four times with an exponential backoff. If Voltus doesn’t quickly receive one of the above success response status code for an event, we mark the event as failed and stop trying to send it to your endpoint.
Why Skinny Payloads?
Skinny payloads refer to webhook payloads that contain a resource URL instead of the resource details. The client then has to make an additional request to obtain the resource details. This mitigates the security risk of receiving resource details from a server acting like Voltus, without requiring you to verify that every request came from Voltus. You can always trust that the data returned by the resource URL is maintained by Voltus.
Testing
You can test your webhook handling of dispatch.create
events with the Create a test dispatch endpoint.
Failure Notifications
If you would like to receive emails and/or text messages if your webhook endpoint fails to respond with a 2xx code, speak with your Voltus representative to be added as a site Dispatch Contact. No notifications are sent if your webhook fails for a Dispatch Verification event.
Note this will also sign you up for emails and/or texts notifying about new or modified dispatches.
Retrieve a webhook
Code samples
curl -X GET https://api.voltus.co/2022-04-15/webhooks \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.get('https://api.voltus.co/2022-04-15/webhooks', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/webhooks");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const headers = {
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/webhooks',
{
method: 'GET',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("GET", "https://api.voltus.co/2022-04-15/webhooks", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
GET /2022-04-15/webhooks
Returns a list of all webhooks that have been created.
All webhooks will be returned in a single request. The values of page
and per_page
have been reserved for future use, but should be ignored at present.
Parameters
Example responses
200 Response
{
"page": 0,
"perPage": 0,
"webhooks": []
}
Response Attributes
page
integer
Page number. Reserved for future use, should ignore for now.
perPage
integer
Number of items per page. Reserved for future use, should ignore for now.
webhooks
array
events
array
List of events that will be posted to this webhook. Currently only `dispatch.create` and `dispatch.update` are supported.
name
string
Name of the event
id
string
Unique identifier for this webhook
url
string
URL that this webhook will send requests to
Create a webhook
Code samples
curl -X POST https://api.voltus.co/2022-04-15/webhooks \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.post('https://api.voltus.co/2022-04-15/webhooks', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/webhooks");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const inputBody = '{
"events": [
{
"name": "dispatch.create"
},
{
"name": "dispatch.update"
}
],
"url": "https://example.com/listeners/voltus"
}';
const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/webhooks',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Content-Type": []string{"application/json"},
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("POST", "https://api.voltus.co/2022-04-15/webhooks", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
POST /2022-04-15/webhooks
Create a webhook by providing the events you'd like to listen for, and the URL where you'd like to receive notifications. When a webhook is created, we will send a test request to the provided URL. If it does not succeed, the webhook creation request will fail.
Body parameter
{
"events": [
{
"name": "dispatch.create"
},
{
"name": "dispatch.update"
}
],
"url": "https://example.com/listeners/voltus"
}
Parameters
body
object
events
array
List of events that will be posted to this webhook. Currently only `dispatch.create` and `dispatch.update` are supported.
name
string
Name of the event
url
string
URL that this webhook will send requests to
Example responses
200 Response
{
"events": [
{
"name": "dispatch.create"
},
{
"name": "dispatch.update"
}
],
"id": "dsft58ga",
"url": "https://example.com/listeners/voltus"
}
Response Attributes
events
array
List of events that will be posted to this webhook. Currently only `dispatch.create` and `dispatch.update` are supported.
name
string
Name of the event
id
string
Unique identifier for this webhook
url
string
URL that this webhook will send requests to
Delete a webhook
Code samples
curl -X DELETE https://api.voltus.co/2022-04-15/webhooks/{id} \
-H 'Accept: application/json' \
-H 'X-Voltus-API-Key: API_KEY' \
import requests
headers = {
'Accept': 'application/json',
'X-Voltus-API-Key': 'API_KEY',
}
r = requests.delete('https://api.voltus.co/2022-04-15/webhooks/{id}', headers=headers)
print(r.json())
URL obj = new URL("https://api.voltus.co/2022-04-15/webhooks/{id}");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("DELETE");
int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
const headers = {
'Accept':'application/json',
'X-Voltus-API-Key':'API_KEY',
};
fetch('https://api.voltus.co/2022-04-15/webhooks/{id}',
{
method: 'DELETE',
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
package main
import (
"bytes"
"net/http"
)
func main() {
headers := map[string][]string{
"Accept": []string{"application/json"},
"X-Voltus-API-Key": []string{"API_KEY"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("DELETE", "https://api.voltus.co/2022-04-15/webhooks/{id}", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
DELETE /2022-04-15/webhooks/{id}
Permanently deletes a webhook. It cannot be undone.
Parameters
id
string
Unique identifier for this webhook
Example responses
200 Response
{
"events": [
{
"name": "dispatch.create"
},
{
"name": "dispatch.update"
}
],
"id": "dsft58ga",
"url": "https://example.com/listeners/voltus"
}
Response Attributes
events
array
List of events that will be posted to this webhook. Currently only `dispatch.create` and `dispatch.update` are supported.
name
string
Name of the event
id
string
Unique identifier for this webhook
url
string
URL that this webhook will send requests to
OpenADR
Overview
Voltus provides an OpenADR 2.0a implementation for receiving dispatch events. This section specifies how Voltus interprets certain OpenADR fields. For unlisted fields, consult the OpenADR specification.
Refer to OpenADR at Voltus for more information.
Voltus Implementation
<?xml version="1.0" encoding="utf-8"?>
<oadr:oadrDistributeEvent
xmlns:ei="http://docs.oasis-open.org/ns/energyinterop/201110"
xmlns:emix="http://docs.oasis-open.org/ns/emix/2011/06"
xmlns:oadr="http://openadr.org/oadr-2.0a/2012/07"
xmlns:pyld="http://docs.oasis-open.org/ns/energyinterop/201110/payloads"
xmlns:strm="urn:ietf:params:xml:ns:icalendar-2.0:stream"
xmlns:xcal="urn:ietf:params:xml:ns:icalendar-2.0">
<ei:eiResponse>
<ei:responseCode>200</ei:responseCode>
<ei:responseDescription>OK</ei:responseDescription>
<pyld:requestID>2019-06-20T18:59:56-0500--ALC_VEN</pyld:requestID>
</ei:eiResponse>
<pyld:requestID>5b445014-6163-46fe-b005-1b386f1f760d</pyld:requestID>
<ei:vtnID>voltus_partner_vtn</ei:vtnID>
<oadr:oadrEvent>
<ei:eiEvent>
<ei:eventDescriptor>
<ei:eventID>377</ei:eventID>
<ei:modificationNumber>0</ei:modificationNumber>
<ei:priority>0</ei:priority>
<ei:eiMarketContext>
<emix:marketContext>https://voltus.co</emix:marketContext>
</ei:eiMarketContext>
<ei:createdDateTime>2019-06-20T18:04:11Z</ei:createdDateTime>
<ei:eventStatus>completed</ei:eventStatus>
<ei:testEvent>false</ei:testEvent>
<ei:vtnComment>Voltus OpenADR 2.0a</ei:vtnComment>
</ei:eventDescriptor>
<ei:eiActivePeriod>
<xcal:properties>
<xcal:dtstart>
<xcal:date-time>2019-06-20T19:00:00Z</xcal:date-time>
</xcal:dtstart>
<xcal:duration>
<xcal:duration>PT4H0M0S</xcal:duration>
</xcal:duration>
<xcal:tolerance>
<xcal:tolerate>
<xcal:startafter>PT0H0M0S</xcal:startafter>
</xcal:tolerate>
</xcal:tolerance>
<ei:x-eiNotification>
<xcal:duration>PT0H0M0S</xcal:duration>
</ei:x-eiNotification>
<ei:x-eiRampUp>
<xcal:duration>PT0H0M0S</xcal:duration>
</ei:x-eiRampUp>
<ei:x-eiRecovery>
<xcal:duration>PT0H0M0S</xcal:duration>
</ei:x-eiRecovery>
</xcal:properties>
<xcal:components
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
</ei:eiActivePeriod>
<ei:eiEventSignals>
<ei:eiEventSignal>
<ei:signalName>simple</ei:signalName>
<ei:signalType>level</ei:signalType>
<ei:signalID>SIGO</ei:signalID>
<strm:intervals>
<ei:interval>
<xcal:duration>
<xcal:duration>PT4H0M0S</xcal:duration>
</xcal:duration>
<xcal:uid>
<xcal:text>0</xcal:text>
</xcal:uid>
<ei:signalPayload>
<ei:payloadFloat>
<ei:value>1.0</ei:value>
</ei:payloadFloat>
</ei:signalPayload>
</ei:interval>
</strm:intervals>
<ei:currentValue>
<ei:payloadFloat>
<ei:value>0.0</ei:value>
</ei:payloadFloat>
</ei:currentValue>
</ei:eiEventSignal>
</ei:eiEventSignals>
<ei:eiTarget>
<ei:resourceID>resource_1</ei:resourceID>
<ei:resourceID>resource_2</ei:resourceID>
<ei:resourceID>resource_3</ei:resourceID>
</ei:eiTarget>
</ei:eiEvent>
<oadr:oadrResponseRequired>always</oadr:oadrResponseRequired>
</oadr:oadrEvent>
</oadr:oadrDistributeEvent>
Payload Configuration
This section describes how Voltus uses some of the main payload fields. Each heading specifically references fields from the openADR 2.0a specification. Some level of customization is possible depending on partner needs.
marketContext
the same value for all dispatches: https://voltus.co
.
There is no need to modify your VEN to support new programs.
Signal Level
We use the signal level (found in eiEventSignals:eiEventSignal:eiCurrentValue:eiPayloadFloat
) to indicate the level of curtailment and can be configured for different program types. Your account manager will work with you to determine optimal curtailment strategies.
Priority
Specifies the priority of events. By default, events are prioritized in the order they are created.
eiTarget
A list of resourceIDs. Identifies the assets to be dispatched.
resourceID
Corresponds to site.customer_location_id
responseRequired
All events have responseRequired set to true. This means we expect you to send an oadrResponse
payload for every dispatch signal you receive from us, even if you have seen it before and the modification number has not increased.