Overview
OpenADR 3 is the OpenADR Alliance standard for demand response and dynamic pricing over a REST API. A utility or aggregator runs a VTN (Virtual Top Node). Sites and devices connect to it as VENs (Virtual End Nodes): they read programs and events (price signals, load-shed requests, import / export capacity limits, GHG signals) and send reports (measured usage, baselines, status) back. A business logic (BL) client on the utility side creates the events. The OpenAdr3Vtn connector maps 26 MQTT topics onto the OpenADR 3.0.1 API: one token request, 12 read events and 13 write events. With it, a Coreflux broker can act as the VEN for a plant or building: it picks up DR events on MQTT, hands them to the controls that shed or shift load, and reports the measured response to the utility. Each event publishes the VTN reply on<topic>/response.
| Event | Topic | Request | Use |
|---|---|---|---|
FetchToken | energy/openadr/auth/token | POST /auth/token | OAuth 2.0 client credentials -> access_token (Bearer), expires_in; form body grant_type=client_credentials&client_id=&client_secret=&scope= |
ListPrograms | energy/openadr/program/list | GET /programs?{query} | DR programs the client may read (programName, intervalPeriod, payloadDescriptors, targets); query targetType=&targetValues=&skip=&limit= |
GetProgram | energy/openadr/program/get | GET /programs/{id} | One program by id |
ListEvents | energy/openadr/event/list | GET /events?{query} | DR events: intervals with payloads (PRICE, SIMPLE, IMPORT_CAPACITY_LIMIT, GHG …), priority, targets; query programID=&targetType=&targetValues=&skip=&limit= |
GetEvent | energy/openadr/event/get | GET /events/{id} | One event by id |
CreateEvent | energy/openadr/event/create | POST /events | WRITE (business logic, write_events): publish a DR event to the targeted VENs; body {programID, intervals, …} |
UpdateEvent | energy/openadr/event/update | PUT /events/{id} | WRITE (business logic): replace an event (full object) |
DeleteEvent | energy/openadr/event/delete | DELETE /events/{id} | WRITE (business logic): delete (cancel) an event |
ListReports | energy/openadr/report/list | GET /reports?{query} | Reports; query programID=&eventID=&clientName=&skip=&limit= |
GetReport | energy/openadr/report/get | GET /reports/{id} | One report by id |
CreateReport | energy/openadr/report/create | POST /reports | WRITE (VEN, write_reports): telemetry back to the VTN; body {programID, eventID, clientName, resources[{resourceName, intervals}]} |
UpdateReport | energy/openadr/report/update | PUT /reports/{id} | WRITE (VEN): replace a report (full object) |
DeleteReport | energy/openadr/report/delete | DELETE /reports/{id} | WRITE (VEN): delete a report |
ListSubscriptions | energy/openadr/subscription/list | GET /subscriptions?{query} | Subscriptions; query programID=&clientName=&targetType=&targetValues=&objects=&skip=&limit= |
GetSubscription | energy/openadr/subscription/get | GET /subscriptions/{id} | One subscription by id |
CreateSubscription | energy/openadr/subscription/create | POST /subscriptions | WRITE: register a webhook; body {clientName, programID, objectOperations[{objects, operations, callbackUrl, bearerToken}]} |
UpdateSubscription | energy/openadr/subscription/update | PUT /subscriptions/{id} | WRITE: replace a subscription (full object) |
DeleteSubscription | energy/openadr/subscription/delete | DELETE /subscriptions/{id} | WRITE: delete a subscription |
ListVens | energy/openadr/ven/list | GET /vens?{query} | VENs visible to the client (venName, attributes, targets); query venName=&targetType=&targetValues=&skip=&limit= |
GetVen | energy/openadr/ven/get | GET /vens/{id} | One VEN by id |
CreateVen | energy/openadr/ven/create | POST /vens | WRITE (write_vens): register a VEN; body {venName, attributes, targets} |
UpdateVen | energy/openadr/ven/update | PUT /vens/{id} | WRITE: replace a VEN (full object) |
ListResources | energy/openadr/resource/list | GET /vens/{ven_id}/resources?{query} | Resources (meters, batteries, chargers) of a VEN; query resourceName=&targetType=&targetValues=&skip=&limit= |
GetResource | energy/openadr/resource/get | GET /vens/{ven_id}/resources/{id} | One resource of a VEN |
CreateResource | energy/openadr/resource/create | POST /vens/{ven_id}/resources | WRITE (write_vens): add a resource to a VEN; body {resourceName, venID, attributes, targets} |
UpdateResource | energy/openadr/resource/update | PUT /vens/{ven_id}/resources/{id} | WRITE: replace a resource (full object) |
/openadr3/3.0.1 (left out in the table).
Write events change VTN state.
CreateEvent, UpdateEvent and DeleteEvent need the business-logic role (write_events): a new event is a demand-response instruction to every targeted VEN. Reports, subscriptions and VEN / resource registration are VEN writes. Restrict who can publish to these topics with a rule. This connector leaves out program administration and VEN / resource deletion.Prerequisites
- Access to a VTN: the URL from your utility, aggregator or DR program, or your own VTN (for example the open-source openleadr-rs VTN).
- Client credentials (
client_id,client_secret) for a VEN client, and a business-logic client if you create events. The VTN operator issues them. - The VTN base path. The OpenADR 3.0.1 specification uses
/openadr3/3.0.1. Some VTNs serve the API at a different path or at the root. Change the endpoints if yours does. - Outbound HTTPS from the broker to the VTN.
Authentication
OpenADR 3 uses OAuth 2.0 client credentials.FetchToken posts a form body to auth/token and gets back {"access_token": "...", "token_type": "Bearer", "expires_in": 3600}. Every other event sends Authorization: {value.json.authorization} with the value Bearer <access_token>.
| Secret | Value |
|---|---|
OPENADR_TOKEN_REQUEST | grant_type=client_credentials&client_id=<id>&client_secret=<secret> (add &scope=read_all when the VTN wants a scope) |
OPENADR_AUTHORIZATION | Bearer <access_token>, refreshed before expires_in runs out |
auth/token a convenience endpoint that a commercial VTN may leave out. In that case, get the token from the utility’s authorization server and store it in OPENADR_AUTHORIZATION.
Scopes: read_all (VENs and BL), write_programs and write_events (BL), write_reports (VENs), write_subscriptions and write_vens (VENs and BL).
| Response | Meaning |
|---|---|
400 {"error": "invalid_client"} | Wrong client id or secret on auth/token |
403 problem JSON | Missing or invalid token, or the token’s scope does not allow the call |
404 problem JSON | Unknown object id |
409 problem JSON | The object already exists (for example a duplicate venName) |
Configuration
| Setting | Value |
|---|---|
BASE_ADDRESS | Your VTN host, such as https://vtn.utility.example (placeholder https://vtn.example.com) |
USE_SSL | "true" |
DEFAULT_CONTENT_TYPE | application/json (auth/token sends application/x-www-form-urlencoded) |
| Payload fields | authorization, id (object id), ven_id (resource events), query (pre-encoded query string, "" for none), body (JSON object; a form string for auth/token) |
| Event | Parameters |
|---|---|
ListPrograms | targetType, targetValues, skip, limit |
ListEvents | programID, targetType, targetValues, skip, limit |
ListReports | programID, eventID, clientName, skip, limit |
ListSubscriptions | programID, clientName, targetType, targetValues, objects, skip, limit |
ListVens | venName, targetType, targetValues, skip, limit |
ListResources | resourceName, targetType, targetValues, skip, limit |
intervals, and each interval has payloads such as {"type": "PRICE", "values": [0.17]} or {"type": "IMPORT_CAPACITY_LIMIT", "values": [50]}. intervalPeriod sets the start and duration (ISO 8601, for example PT1H).
Basic Syntax
# Coreflux REST_API template — OpenADR 3.0 (3.0.1) demand response, VEN / business-logic client of a VTN:
# get an OAuth 2.0 client-credentials token, read DR programs and events (price, load shed, GHG and other
# signals), read, create, update and delete reports (telemetry / baselines back to the utility), manage
# subscriptions (webhook callbacks), and read / register VENs and their resources; plus (marked WRITE,
# business-logic role only) create, update and delete events.
# Hugo catalog #180, Tier 2 (energy). Spec: OpenADR Alliance OpenAPI "OpenADR 3 API" 3.0.1 (public copy
# github.com/grid-coordination/openadr3-specification, upstream oadr3-org/specification a35682b; Apache 2.0),
# curated slim in openapi-cache/openadr-3-0.json. Credentials are placeholders only.
# Docs: https://docs.coreflux.org/latest/lot-language/routes/rest-api-apps/openadr-3-0
#
# VTN: every utility / aggregator runs its own VTN. BASE_ADDRESS is a placeholder: set it to your VTN host and
# change /openadr3/3.0.1 in the endpoints if your VTN uses another base path (the spec server is
# http://localhost:8081/openadr3/3.0.1; openleadr-rs serves the API at its root).
# Auth: Authorization: {value.json.authorization} = "Bearer <access_token>" on every call except auth/token.
# Tokens come from OAuth 2.0 client credentials. The VTN may host POST {base}/auth/token (FetchToken, form
# body grant_type=client_credentials&client_id=...&client_secret=...&scope=...) or point to an external
# authorization server (then fetch the token there). Scopes: read_all, write_programs, write_events
# (business logic), write_reports (VEN), write_subscriptions, write_vens. Keep client id / secret in Hub secrets
# (suggested OPENADR_CLIENT_ID, OPENADR_CLIENT_SECRET) and the current token in OPENADR_AUTHORIZATION.
# Errors: 400 / 403 / 404 / 409 / 500 as application/problem+json {type, title, status, detail}.
# Queries: list events with ?{value.json.query} take a pre-encoded query string ("" for none):
# programs targetType=&targetValues=&skip=&limit=
# events programID=&targetType=&targetValues=&skip=&limit=
# reports programID=&eventID=&clientName=&skip=&limit=
# subscriptions programID=&clientName=&targetType=&targetValues=&objects=&skip=&limit=
# vens venName=&targetType=&targetValues=&skip=&limit=
# resources resourceName=&targetType=&targetValues=&skip=&limit=
# Writes (marked WRITE below): reports, subscriptions, VEN / resource registration (VEN role) and events
# (business-logic role: a new event is a demand-response instruction to every targeted VEN). Not packaged:
# program create / update / delete, VEN and resource delete.
# Webhooks: POST subscription/create with objectOperations[].callbackUrl pointing to a Coreflux REST_API server
# route (ENABLE_SERVER) to get pushed notifications; the VTN POSTs {objectType, operation, object} to it.
#
# Payload fields: authorization, id (object id), ven_id (resources), query, body (JSON object; form string for
# auth/token)
# FetchToken body = "grant_type=client_credentials&client_id=ven_client_99&client_secret=<secret>"
# ListEvents query = "programID=0&limit=50"
# CreateReport body = {"programID": "0", "eventID": "0", "clientName": "VEN-999", "resources": [...]}
DEFINE ROUTE OpenAdr3Vtn WITH TYPE REST_API
ADD REST_API_CONFIG
WITH BASE_ADDRESS "https://vtn.example.com"
WITH ENABLE_CLIENT "true"
WITH USE_SSL "true"
WITH REQUEST_TIMEOUT '30'
WITH DEFAULT_CONTENT_TYPE "application/json"
# Smoke: FetchToken -> ListPrograms -> ListEvents?programID= -> CreateReport -> ListReports -> DeleteReport.
ADD EVENT FetchToken
WITH SOURCE_TOPIC "energy/openadr/auth/token"
WITH DESTINATION_TOPIC "energy/openadr/auth/token/response"
WITH METHOD "POST"
WITH ENDPOINT "/openadr3/3.0.1/auth/token"
WITH HEADERS
ADD "Accept" "application/json"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/x-www-form-urlencoded"
WITH BODY "{value.json.body}"
ADD EVENT ListPrograms
WITH SOURCE_TOPIC "energy/openadr/program/list"
WITH DESTINATION_TOPIC "energy/openadr/program/list/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/programs?{value.json.query}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT GetProgram
WITH SOURCE_TOPIC "energy/openadr/program/get"
WITH DESTINATION_TOPIC "energy/openadr/program/get/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/programs/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT ListEvents
WITH SOURCE_TOPIC "energy/openadr/event/list"
WITH DESTINATION_TOPIC "energy/openadr/event/list/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/events?{value.json.query}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT GetEvent
WITH SOURCE_TOPIC "energy/openadr/event/get"
WITH DESTINATION_TOPIC "energy/openadr/event/get/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/events/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
# WRITE: CreateEvent changes VTN state. Restrict who can publish to energy/openadr/event/create.
ADD EVENT CreateEvent
WITH SOURCE_TOPIC "energy/openadr/event/create"
WITH DESTINATION_TOPIC "energy/openadr/event/create/response"
WITH METHOD "POST"
WITH ENDPOINT "/openadr3/3.0.1/events"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: UpdateEvent changes VTN state. Restrict who can publish to energy/openadr/event/update.
ADD EVENT UpdateEvent
WITH SOURCE_TOPIC "energy/openadr/event/update"
WITH DESTINATION_TOPIC "energy/openadr/event/update/response"
WITH METHOD "PUT"
WITH ENDPOINT "/openadr3/3.0.1/events/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: DeleteEvent changes VTN state. Restrict who can publish to energy/openadr/event/delete.
ADD EVENT DeleteEvent
WITH SOURCE_TOPIC "energy/openadr/event/delete"
WITH DESTINATION_TOPIC "energy/openadr/event/delete/response"
WITH METHOD "DELETE"
WITH ENDPOINT "/openadr3/3.0.1/events/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT ListReports
WITH SOURCE_TOPIC "energy/openadr/report/list"
WITH DESTINATION_TOPIC "energy/openadr/report/list/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/reports?{value.json.query}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT GetReport
WITH SOURCE_TOPIC "energy/openadr/report/get"
WITH DESTINATION_TOPIC "energy/openadr/report/get/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/reports/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
# WRITE: CreateReport changes VTN state. Restrict who can publish to energy/openadr/report/create.
ADD EVENT CreateReport
WITH SOURCE_TOPIC "energy/openadr/report/create"
WITH DESTINATION_TOPIC "energy/openadr/report/create/response"
WITH METHOD "POST"
WITH ENDPOINT "/openadr3/3.0.1/reports"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: UpdateReport changes VTN state. Restrict who can publish to energy/openadr/report/update.
ADD EVENT UpdateReport
WITH SOURCE_TOPIC "energy/openadr/report/update"
WITH DESTINATION_TOPIC "energy/openadr/report/update/response"
WITH METHOD "PUT"
WITH ENDPOINT "/openadr3/3.0.1/reports/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: DeleteReport changes VTN state. Restrict who can publish to energy/openadr/report/delete.
ADD EVENT DeleteReport
WITH SOURCE_TOPIC "energy/openadr/report/delete"
WITH DESTINATION_TOPIC "energy/openadr/report/delete/response"
WITH METHOD "DELETE"
WITH ENDPOINT "/openadr3/3.0.1/reports/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT ListSubscriptions
WITH SOURCE_TOPIC "energy/openadr/subscription/list"
WITH DESTINATION_TOPIC "energy/openadr/subscription/list/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/subscriptions?{value.json.query}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT GetSubscription
WITH SOURCE_TOPIC "energy/openadr/subscription/get"
WITH DESTINATION_TOPIC "energy/openadr/subscription/get/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/subscriptions/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
# WRITE: CreateSubscription changes VTN state. Restrict who can publish to energy/openadr/subscription/create.
ADD EVENT CreateSubscription
WITH SOURCE_TOPIC "energy/openadr/subscription/create"
WITH DESTINATION_TOPIC "energy/openadr/subscription/create/response"
WITH METHOD "POST"
WITH ENDPOINT "/openadr3/3.0.1/subscriptions"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: UpdateSubscription changes VTN state. Restrict who can publish to energy/openadr/subscription/update.
ADD EVENT UpdateSubscription
WITH SOURCE_TOPIC "energy/openadr/subscription/update"
WITH DESTINATION_TOPIC "energy/openadr/subscription/update/response"
WITH METHOD "PUT"
WITH ENDPOINT "/openadr3/3.0.1/subscriptions/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: DeleteSubscription changes VTN state. Restrict who can publish to energy/openadr/subscription/delete.
ADD EVENT DeleteSubscription
WITH SOURCE_TOPIC "energy/openadr/subscription/delete"
WITH DESTINATION_TOPIC "energy/openadr/subscription/delete/response"
WITH METHOD "DELETE"
WITH ENDPOINT "/openadr3/3.0.1/subscriptions/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT ListVens
WITH SOURCE_TOPIC "energy/openadr/ven/list"
WITH DESTINATION_TOPIC "energy/openadr/ven/list/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/vens?{value.json.query}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT GetVen
WITH SOURCE_TOPIC "energy/openadr/ven/get"
WITH DESTINATION_TOPIC "energy/openadr/ven/get/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/vens/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
# WRITE: CreateVen changes VTN state. Restrict who can publish to energy/openadr/ven/create.
ADD EVENT CreateVen
WITH SOURCE_TOPIC "energy/openadr/ven/create"
WITH DESTINATION_TOPIC "energy/openadr/ven/create/response"
WITH METHOD "POST"
WITH ENDPOINT "/openadr3/3.0.1/vens"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: UpdateVen changes VTN state. Restrict who can publish to energy/openadr/ven/update.
ADD EVENT UpdateVen
WITH SOURCE_TOPIC "energy/openadr/ven/update"
WITH DESTINATION_TOPIC "energy/openadr/ven/update/response"
WITH METHOD "PUT"
WITH ENDPOINT "/openadr3/3.0.1/vens/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
ADD EVENT ListResources
WITH SOURCE_TOPIC "energy/openadr/resource/list"
WITH DESTINATION_TOPIC "energy/openadr/resource/list/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/vens/{value.json.ven_id}/resources?{value.json.query}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD EVENT GetResource
WITH SOURCE_TOPIC "energy/openadr/resource/get"
WITH DESTINATION_TOPIC "energy/openadr/resource/get/response"
WITH METHOD "GET"
WITH ENDPOINT "/openadr3/3.0.1/vens/{value.json.ven_id}/resources/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
# WRITE: CreateResource changes VTN state. Restrict who can publish to energy/openadr/resource/create.
ADD EVENT CreateResource
WITH SOURCE_TOPIC "energy/openadr/resource/create"
WITH DESTINATION_TOPIC "energy/openadr/resource/create/response"
WITH METHOD "POST"
WITH ENDPOINT "/openadr3/3.0.1/vens/{value.json.ven_id}/resources"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
# WRITE: UpdateResource changes VTN state. Restrict who can publish to energy/openadr/resource/update.
ADD EVENT UpdateResource
WITH SOURCE_TOPIC "energy/openadr/resource/update"
WITH DESTINATION_TOPIC "energy/openadr/resource/update/response"
WITH METHOD "PUT"
WITH ENDPOINT "/openadr3/3.0.1/vens/{value.json.ven_id}/resources/{value.json.id}"
WITH HEADERS
ADD "Accept" "application/json"
ADD "Authorization" "{value.json.authorization}"
ADD "User-Agent" "Coreflux-Broker REST_API route (https://www.coreflux.org)"
ADD "Content-Type" "application/json"
WITH BODY "{value.json.body}"
Examples
Get a token: publish{"body": "grant_type=client_credentials&client_id=ven_client_99&client_secret=<secret>"}
energy/openadr/auth/token and read access_token from energy/openadr/auth/token/response.
Read the events of one program: publish
{"authorization": "Bearer <access_token>", "query": "programID=0&limit=50"}
energy/openadr/event/list.
Report measured usage for an event: publish
{"authorization": "Bearer <access_token>", "body": {"programID": "0", "eventID": "12", "clientName": "porto-plant", "resources": [{"resourceName": "main-meter", "intervals": [{"id": 0, "payloads": [{"type": "USAGE", "values": [412.5]}]}]}]}}
energy/openadr/report/create.
Actions that keep a token and poll events:
# Example actions for the OpenAdr3Vtn route (docs page). Not part of the route template.
# Secrets: OPENADR_TOKEN_REQUEST = "grant_type=client_credentials&client_id=<id>&client_secret=<secret>" (form body
# for auth/token) and OPENADR_AUTHORIZATION = "Bearer <access_token>" (current token; refresh it before expires_in).
# Program id "0" is a placeholder. Use an id from program/list.
# Inline JSON object literals are not supported when publishing to a topic, so the request payload is a COLLAPSED
# model sent with PUBLISH MODEL.
DEFINE MODEL OpenAdrRequest COLLAPSED
ADD STRING "authorization"
ADD STRING "id"
ADD STRING "query"
DEFINE MODEL OpenAdrTokenRequest COLLAPSED
ADD STRING "body"
DEFINE ACTION OpenAdrTokenHourly
ON EVERY 1 HOUR DO
PUBLISH MODEL OpenAdrTokenRequest TO "energy/openadr/auth/token" WITH
body = (GET SECRET "OPENADR_TOKEN_REQUEST")
DEFINE ACTION OpenAdrEventsEvery5Minutes
ON EVERY 5 MINUTES DO
PUBLISH MODEL OpenAdrRequest TO "energy/openadr/event/list" WITH
authorization = (GET SECRET "OPENADR_AUTHORIZATION")
id = ""
query = "programID=0&limit=50"
DEFINE ACTION OpenAdrProgramsDaily
ON EVERY 1 DAY DO
PUBLISH MODEL OpenAdrRequest TO "energy/openadr/program/list" WITH
authorization = (GET SECRET "OPENADR_AUTHORIZATION")
id = ""
query = "limit=50"
energy/openadr/# with a rule, because these payloads carry the client secret and token.
Notifications (webhooks)
Instead of polling, create a subscription withsubscription/create. Its objectOperations[].callbackUrl points to a Coreflux REST API server connector (ENABLE_SERVER), and the VTN posts {"objectType", "operation", "object"} to it whenever an event, program or report changes. OpenADR 3.1 also lets a VTN publish these notifications to an MQTT broker, which fits a Coreflux broker directly.
Notes
- Versions: this connector follows OpenADR 3.0.1. OpenADR 3.1.0 (August 2025) keeps the same objects but uses a
targetsquery parameter, moves resources to a top-level/resourcespath, and addsauth/serverand MQTT notifiers. Check which version your VTN runs. - Spec source: the OpenAPI document is published by the OpenADR Alliance (Apache 2.0). A public copy is on GitHub.
- Polling: DR events are usually published hours or a day ahead. Polling
event/listevery 5 minutes is plenty; subscriptions remove the need to poll. - Testing status: the connector has only been tested against the specification and a local mock VTN. Results against a real VTN are still to come.

