Swagger links

Links below are OpenAPI documentation for the services you will need to communicate through.

Egress service: https://waas-stage.azure-api.net/egress/swagger
Provisioning service: https://waas-stage.azure-api.net/provisioning/swagger
Ingest service: https://waas-stage.azure-api.net/ingest/swagger
Notification service: https://waas-stage.azure-api.net/notifications/swagger

Picture

Watts as a Service

This document serves as a draft for API documentation

Getting started

curl --location --request POST 'https://login.microsoftonline.com/c4101101-d8bc-426b-b3ea-4db330a69f92/oauth2/v2.0/token' \ 
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=[ClientId]' \
--data-urlencode 'client_secret=[ClientSecret]' \
--data-urlencode 'scope=[Scope]'

Acquiring authentication token

We use Azure OAuth for authentication purposes.

Therefore, a JWT Bearer token will be needed in order to authenticate with all APIs.

Token is acquired through an HTTP endpoint (as in example)
(ClientId, ClientSecret and Scope will be provided by watts administrator upon request.)

var confidentialClient = ConfidentialClientApplicationBuilder
.Create("[ClientId]")
.WithClientSecret("[ClientSecret]")
.WithAuthority("https://login.microsoftonline.com/c4101101-d8bc-426b-b3ea-4db330a69f92")
.Build();

var accessTokenResponse = await confidentialClient.AcquireTokenForClient(
new[] { "https://energybutlerb2c.onmicrosoft.com/dcb4ab73-bf15-49e0-ac12-d75601faa377/.default" })
.ExecuteAsync();

var token = accessTokenResponse.AccessToken;

Alternatively, microsoft libraries (Microsoft.Identity.Client) could be used

var httpClient = new HttpClient(); 
defaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("bearer", token);

await httpClient.GetAsync("https://waas-stage.azure-api.net/egress/<..>");

Calling an api

To call one of our endpoints, simply just add acquired token in the Authorization header as per OAuth JWT standard.

Preparing webhook to receive notifications

Schema

We use CloudEvents v1.0 schema to send notifications

Example event

{
"id": "9aeb0fdf-c01e-0131-0922-9eb54906e209",
"type": "ConsumptionsAvailable",
"source": "/notification-service",
"data": { "deviceId": "eb093330-f5bc-4c06-82e5-3cf671d3412e" },
"time": "2021-11-05T12:51:19.8506671+00:00",
"specversion": "1.0",
"trackingid": "ea626280-b8b7-4636-a5bb-0c913d5905b2",
"datacontenttype": "application/json"
}

Endpoint requirements

The Handshake

The endpoint should support Abuse Protection - when assigning the webhook, the HTTP OPTIONS request will be sent to the provided endpoint.

When the request is received there are a few ways to complete handshake:

Synchronous handshake

  1. Request will contain Webhook-Request-Origin header

  2. Take the header value and put it under WebHook-Allowed-Origin header for a response

  3. Return response with 200 OK status code

Asynchronous handshake

  1. Request will contain WebHook-Request-Callback header with an URL

  2. The delivery target grants permission by issuing an HTTPS GET or POST request against the given URL. The HTTP GET request can be performed manually using a browser client.

Authentication

Requests to provided endpoint will include the header tenant provides

Synchronous handshake implementation with Azure functions

Notification processing (handle POST request)

public async Task<IActionResult> ProcessNotifications(
[HttpTrigger(AuthorizationLevel.Anonymous, nameof(HttpMethods.Post), Route = Route)] HttpRequest req,
CancellationToken cancellationToken)
{
if (!req.Headers.TryGetValue("some-auth-header", out var authHeaderValue) || !authHeaderValue.Equals(expectedValue))
{
return new ForbidResult();
}

var cloudEvents = Azure.Messaging.CloudEvent.ParseMany(await BinaryData.FromStreamAsync(req.Body, cancellationToken));

foreach (var cloudEvent in cloudEvents)
{
await HandleCloudEvent(cloudEvent, cancellationToken);
}

return new AcceptedResult();
}

Handshake (handle OPTIONS request)

public IActionResult RequestNotificationsDelivery(
[HttpTrigger(AuthorizationLevel.Anonymous, nameof(HttpMethods.Options), Route = Route)] HttpRequest req)
{
if (req.Headers.TryGetValue("some-auth-header", out var authHeaderValue)
&& authHeaderValue.Equals(expectedValue)
&& req.Headers.TryGetValue("WebHook-Request-Origin", out var requestOriginValue)
&& requestOriginValue.Equals("eventgrid.azure.net"))
{
req.HttpContext.Response.Headers.Add("WebHook-Allowed-Origin", requestOriginValue);
return new OkResult();
}

return new ForbidResult();
}


Data ingest pipeline

1. Provision a device

Send a request to provisioning service to persist device information and metadata required for forecasting.

2. Send consumptios

Send a request to Ingest API with batch of consumptions.

 https://waas-stage.azure-api.net/ingest/swagger

Keep in mind: Timeseries data must have a UTC timestamp

3. Get a notification callback

Subscribe to notification webhook to receive callback for ocurring events.

Some of most relevant events include:

  • ConsumptionsAvailable

  • ElectricityConsumptionForecastAvailable

  • ElectricityBaseLoadForecastAvailable

  • ElectricityBaseLoadEstimateAvailable

4. Fetch data

When necessary event is received, data is available for fetching in Egress API.