Locations, Areas, and Price Signals
This guide covers how to set up locations with areas, activate zones, and push price signals. These are prerequisites for flex shape computation.
Copy linkLocations and Areas
Locations represent physical sites where assets are installed. Each location has two geographic classification fields, both set by you:
zone— the electricity bidding zone the location belongs to (e.g.,NO1,SE3,FI). This is the top-level grouping for flex shapes.area— the sub-area within the zone. Areas are how you scope price signals to groups of locations. The area value is opaque to Enode; use whatever grouping is meaningful to your integration (forecast areas, grid areas, metering grid areas, etc.).
Copy linkCreating a Location
POST /users/{userId}/locations
Content-Type: application/json
{
"name": "Household A",
"latitude": 59.911,
"longitude": 10.753,
"timezoneName": "Europe/Oslo",
"zone": "NO1",
"area": "FA_12"
}
{
"id": "4eaeb363-296d-4ccc-a973-7805e6f400bd",
"userId": "b3a49e7c-8c9a-4b1f-8f4a-2e5d3c6a7b8c",
"name": "Household A",
"latitude": 59.911,
"longitude": 10.753,
"timezoneName": "Europe/Oslo",
"zone": "NO1",
"area": "FA_12",
"createdAt": "2024-06-14T10:00:00Z"
}
Both zone and area are required.
Copy linkSingle-Area Zones
If your market has a single pricing zone with no meaningful geographic subdivisions, set the area to the same value as the zone:
{
"zone": "BE",
"area": "BE"
}
This keeps the model uniform — you push one price signal to one area. No special configuration is needed.
Copy linkUpdating a Location
PUT /locations/{locationId}
Content-Type: application/json
{
"zone": "NO2",
"area": "FA_13"
}
All fields on the update payload are optional. You can update zone and area independently of other location fields.
Copy linkHow Zone and Area Are Used
The zone is the top-level grouping — flex shapes are queried per zone (GET /flex/shape/{zoneId}).
The area is how price signals are scoped to locations. Each location has exactly one area, so there is no ambiguity about which price applies. Areas serve two purposes:
- Price signals — prices are pushed per area, and each location receives the price signal for its area
- Shape filtering — query the flex shape endpoint with an
areaparameter to get shapes for a geographic subset of a zone
Copy linkZone Activation
Before price signals can be pushed or flex shapes computed for a zone, the zone must be activated. Activation sets the currency that all subsequent price signal and tariff data must use.
POST /flex/zones/{zoneId}
Content-Type: application/json
{
"currency": "EUR"
}
{
"id": "NO1",
"currency": "EUR",
"createdAt": "2024-06-14T10:00:00Z",
"updatedAt": "2024-06-14T10:00:00Z"
}
Activation requires:
- At least one location exists in the zone.
- All locations have an area. Since prices are always pushed per area, every location must have an area before the zone can accept price signals.
The currency is validated on every subsequent price signal push and tariff formula configuration, ensuring all price inputs for a zone use the same currency.
Copy linkListing Zones
GET /flex/zones
Returns all activated zones.
Copy linkDiscovering Areas
Areas are not explicitly created — they exist implicitly as values on locations. Use this endpoint to see all areas in a zone, how many locations each has, and whether a price signal has been pushed:
GET /flex/zones/NO1/areas
{
"zoneId": "NO1",
"areas": [
{ "areaId": "FA_12", "locationCount": 142, "hasPriceSignal": true },
{ "areaId": "FA_13", "locationCount": 89, "hasPriceSignal": true },
{ "areaId": "FA12", "locationCount": 3, "hasPriceSignal": false }
]
}
Copy linkPrice Signals
Price signals represent the wholesale price signal — day-ahead prices, forecasted day-ahead prices, or forecasted imbalance prices. From the API's perspective these are all the same data type: a timeseries of prices over future time slots. The distinction matters only for how often you update them.
Copy linkPushing Price Signals
Push a price signal for an area within a zone:
PUT /flex/price-signals/{zoneId}/areas/{areaId}
Content-Type: application/json
{
"currency": "EUR",
"to": "2024-06-16T00:00:00Z",
"values": [
{ "at": "2024-06-15T00:00:00Z", "price": 0.045 },
{ "at": "2024-06-15T01:00:00Z", "price": 0.038 },
{ "at": "2024-06-15T02:00:00Z", "price": 0.029 },
{ "at": "2024-06-15T06:00:00Z", "price": 0.052 },
{ "at": "2024-06-15T12:00:00Z", "price": 0.089 },
{ "at": "2024-06-15T18:00:00Z", "price": 0.142 }
]
}
Each location receives the price signal for its area. If multiple areas share the same prices, use the bulk endpoint to push once for all of them.
Copy linkBulk Push
Push the same price signal to multiple areas in one call:
PUT /flex/price-signals/{zoneId}/areas
Content-Type: application/json
{
"areas": ["FA_01", "FA_02", "FA_03"],
"currency": "EUR",
"to": "2024-06-16T00:00:00Z",
"values": [
{ "at": "2024-06-15T00:00:00Z", "price": 0.045 },
{ "at": "2024-06-15T01:00:00Z", "price": 0.038 }
]
}
Each area gets its own stored price signal — this is a convenience to reduce HTTP calls.
Copy linkPush Format
Timestamps are UTC. Each value defines the price from its timestamp until the next value (step function). The body specifies a replacement window — all existing data in the window is replaced with the provided values.
- At least one value is required
- Values must be ordered chronologically
- The first value's
atdefines the start of the replacement window - Data outside
[firstValue.at, to)is untouched - Currency must match the zone's configured currency
The currency field in the push payload is validated against the zone's configured currency. This serves as an explicit confirmation, catching integration bugs where a system is accidentally pointed at the wrong zone.
Copy linkWhat Happens on Push
Each price signal push updates the optimization target for all assets at locations in that area. The system incorporates the new prices into scheduling decisions as follows:
- Future schedules reflect the updated prices immediately. Any time slot where a command has not yet been sent is free to change at no cost.
- Currently executing commands are not interrupted unless the price change is significant enough to justify it. The system accounts for the cost and reliability of sending new commands — a small price fluctuation will not cause unnecessary churn, while a large price spike will trigger re-scheduling where it makes economic sense.
- Each asset is evaluated independently. An asset with a strong track record of responding to commands may be re-scheduled more readily than one with a patchy history, even if both are in the same area.
Push forecasts as often as you have updated data. Frequent small updates are preferred over infrequent large ones — the system is designed to handle high-frequency price updates efficiently.
Copy linkReading Price Signals
Retrieve the current price signal for an area over a time range:
GET /flex/price-signals/{zoneId}/areas/{areaId}?from=2024-06-15T00:00:00Z&to=2024-06-16T00:00:00Z
{
"zoneId": "NO1",
"areaId": "FA_12",
"currency": "EUR",
"from": "2024-06-15T00:00:00Z",
"to": "2024-06-16T00:00:00Z",
"values": [
{ "at": "2024-06-15T00:00:00Z", "price": 0.045 },
{ "at": "2024-06-15T01:00:00Z", "price": 0.038 },
{ "at": "2024-06-15T02:00:00Z", "price": 0.029 },
{ "at": "2024-06-15T06:00:00Z", "price": 0.052 },
{ "at": "2024-06-15T12:00:00Z", "price": 0.089 },
{ "at": "2024-06-15T18:00:00Z", "price": 0.142 }
]
}
Copy linkEndpoint Reference
| Task | Method | Endpoint |
|---|---|---|
| Create location | POST | /users/{userId}/locations |
| Update location | PUT | /locations/{locationId} |
| Activate zone | POST | /flex/zones/{zoneId} |
| Get zone | GET | /flex/zones/{zoneId} |
| List zones | GET | /flex/zones |
| List areas in zone | GET | /flex/zones/{zoneId}/areas |
| Push price signal | PUT | /flex/price-signals/{zoneId}/areas/{areaId} |
| Push price signal (bulk) | PUT | /flex/price-signals/{zoneId}/areas |
| Read price signal | GET | /flex/price-signals/{zoneId}/areas/{areaId} |
Define household segments and retrieve the flex shape forecast