# 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. ## Locations 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.). ### Creating a Location ```http 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" } ``` ```json { "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. ### Single-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: ```json { "zone": "BE", "area": "BE" } ``` This keeps the model uniform — you push one price signal to one area. No special configuration is needed. ### Updating a Location ```http 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. ### How 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 `area` parameter to get shapes for a geographic subset of a zone ## Zone 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. ```http POST /flex/zones/{zoneId} Content-Type: application/json { "currency": "EUR" } ``` ```json { "id": "NO1", "currency": "EUR", "createdAt": "2024-06-14T10:00:00Z", "updatedAt": "2024-06-14T10:00:00Z" } ``` Activation requires: 1. **At least one location** exists in the zone. 2. **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. ### Listing Zones ```http GET /flex/zones ``` Returns all activated zones. ### Discovering 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: ```http GET /flex/zones/NO1/areas ``` ```json { "zoneId": "NO1", "areas": [ { "areaId": "FA_12", "locationCount": 142, "hasPriceSignal": true }, { "areaId": "FA_13", "locationCount": 89, "hasPriceSignal": true }, { "areaId": "FA12", "locationCount": 3, "hasPriceSignal": false } ] } ``` Watch for typos: In the example above, 3 locations have area `FA12` (missing underscore) with no price signal — likely a typo. Locations in areas without a price signal cannot be optimized. ## Price 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. ### Pushing Price Signals Push a price signal for an area within a zone: ```http 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. ### Bulk Push Push the same price signal to multiple areas in one call: ```http 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. ### Push 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 `at` defines 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. ### What 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. ### Reading Price Signals Retrieve the current price signal for an area over a time range: ```http GET /flex/price-signals/{zoneId}/areas/{areaId}?from=2024-06-15T00:00:00Z&to=2024-06-16T00:00:00Z ``` ```json { "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 } ] } ``` ## Endpoint 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}` | [Next: Segments and Flex Shape](https://flex.developers.enode.com/docs/flex-shape/segments-and-flex-shape)