Sites
As a Powernaut partner, you play a crucial role in connecting flexible energy resources to the Powernaut Open VPP. The primary entity you will manage is a site. This represents a physical location (like a household or office building) with a meter, where one or more energy resources are installed. Each site includes a meter (head meter, digital meter), which enables grid operators and suppliers to monitor energy usage and facilitate appropriate remuneration.
These sites, once matched with an upstream energy supplier's site, enable the provision of grid flexibility services.
The activities recorded by the meters at these sites are crucial for providing flexibility. Adjusting consumption or production at these points helps maintain grid stability.
Identifiers​
Each site is identified by a unique grid identifier, typically associated with the end-consumer's main meter. This identifier is essential for matching your site with an upstream supplier's site and varies by country.
Country | Identifier |
---|---|
Belgium | EAN |
Netherlands | EAN |
Luxembourg | EAN |
France | PRM |
United Kingdom | MPAN |
Ireland | MPRN |
Italy | POD |
Spain | CUPS |
Multiple Meters: A site typically identifies a single head (digital) meter. If a location has multiple head meters, you should register them as distinct sites.
Managing Sites​
To integrate energy resources and enable flexibility services, you'll first need to create and manage sites.
Creating a Site​
To create a site, you need at least its grid identifier.
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
import json
url = "https://api.powernaut.io/v1/connect/sites"
payload = json.dumps({
"location": {
"address": "Dok Noord, Gent, Belgium"
},
"supply_points": [
{
"grid_identifier": "EAN1234567890",
"direction": "bidirectional"
}
]
})
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const raw = JSON.stringify({
"location": {
"address": "Dok Noord, Gent, Belgium"
},
"supply_points": [
{
"grid_identifier": "EAN1234567890",
"direction": "bidirectional"
}
]
});
const requestOptions = {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/sites", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"location\":{\"address\":\"Dok Noord, Gent, Belgium\"},\"supply_points\":[{\"grid_identifier\":\"EAN1234567890\",\"direction\":\"bidirectional\"}]}");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/sites")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/sites"
method := "POST"
payload := strings.NewReader(`{"location":{"address":"Dok Noord, Gent, Belgium"},"supply_points":[{"grid_identifier":"EAN1234567890","direction":"bidirectional"}]}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.powernaut.io/v1/connect/sites");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var content = new StringContent("{\"location\":{\"address\":\"Dok Noord, Gent, Belgium\"},\"supply_points\":[{\"grid_identifier\":\"EAN1234567890\",\"direction\":\"bidirectional\"}]}", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location 'https://api.powernaut.io/v1/connect/sites' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{"location":{"address":"Dok Noord, Gent, Belgium"},"supply_points":[{"grid_identifier":"EAN1234567890","direction":"bidirectional"}]}'
Upon successful creation, your new site will have initial statuses:
status
: This will default tono_resources
. This indicates that while the site exists, no specific energy resources (like batteries, EV chargers, etc.) have been registered under it yet. You'll do this in a subsequent step.upstream_status
: This status reflects the state of the corresponding upstream (supplier's) site and Contract:- If no supplier has registered an upstream site with a matching grid identifier, the
upstream_status
will beno_upstream_registered
. - If a supplier's upstream site exists but doesn't yet have an active (or pending) contract, the
upstream_status
might beno_contract
. - Other
upstream_status
values (likecontract_needs_agreement
,contract_pending
,contract_active
,contract_inactive
) will appear as the supplier manages their side of the setup.
- If no supplier has registered an upstream site with a matching grid identifier, the
The endpoint /v1/connect/sites
is used to manage sites from your perspective.
Understanding Site Statuses​
As a Powernaut partner, you'll primarily interact with two types of statuses for your sites:
-
status
(Your site's Resource Status):no_resources
: The site is created, but no flexible energy resources have been added to it yet.onboarded
: You have successfully added one or more resources to this site, and it's ready from your end.- (Note:
paused
anddeleted
statuses are defined for future enhancements to allow temporary deactivation or soft deletion of sites, but their full backend logic is not yet implemented.)
-
upstream_status
(Status of the Linked Supplier's Upstream site/Contract): This status gives you visibility into the supplier's (upstream) side. It automatically updates as the supplier manages their site and Contract for the matching grid identifier.no_upstream_registered
: No energy supplier has created an upstream site entry for this grid identifier yet.no_contract
: A supplier has created an upstream site, but there's no contract (or no current/future contract) associated with it.contract_needs_agreement
: A supplier has set up a contract for their upstream site, but it's awaiting agreement from the end-customer.contract_pending
: An agreed contract exists for the upstream site, but its start date is in the future.contract_active
: The supplier has an active contract for the matching upstream site. If your sitestatus
is alsoonboarded
, flexibility services can typically commence.contract_inactive
: The supplier's contract for the matching upstream site has expired or been terminated.
For a site that you manage to be fully operational in the VPP, its status
should ideally be onboarded
, and its upstream_status
should reflect an contract_active
state.
Deleting a Site​
You can delete a site, for example, if you are no longer serving the end-consumer or the physical assets are decommissioned.
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
url = "https://api.powernaut.io/v1/connect/sites/<uuid>"
payload = {}
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("DELETE", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const requestOptions = {
method: "DELETE",
headers: myHeaders,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/sites/<uuid>", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/sites/<uuid>")
.method("DELETE", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/sites/<uuid>"
method := "DELETE"
client := &http.Client {
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.powernaut.io/v1/connect/sites/<uuid>");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location --request DELETE 'https://api.powernaut.io/v1/connect/sites/<uuid>' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>'
Deleting a site is a permanent action and will disassociate any linked resources. Ensure this is the intended action.
Listing Sites​
You can fetch details for a single site using its ID, or list all sites associated with your organization:
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
url = "https://api.powernaut.io/v1/connect/sites?page=1&page_size=10"
payload = {}
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/sites?page=1&page_size=10", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/sites?page=1&page_size=10")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/sites?page=1&page_size=10"
method := "GET"
client := &http.Client {
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.powernaut.io/v1/connect/sites?page=1&page_size=10");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location 'https://api.powernaut.io/v1/connect/sites?page=1&page_size=10' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>'
This endpoint supports pagination. The response will include the status
and upstream_status
for each site.
Now that we've learned how to register and understand the status of sites, let's continue by registering flexible resources for this site.