Skip to content

Energy Management API

Base paths:

  • /core/api/communities/{community_id}/energy_management - module discovery, configurations, setpoints, offers, member energy and baseline data
  • /core/api/flexibility_zones - flexibility zones shared by all communities
  • /core/api/communities/{community_id}/setpoints - legacy battery setpoints

The Energy Management System (EMS) is a pluggable set of optimization and flexibility modules, each identified by a module_type. A community activates a module by creating a configuration; the module then produces (or receives from an external service) setpoints, member energy data, baselines and flexibility offers. For the concepts see Energy Management; for the individual modules and their config_data see Optimization.

Authentication. Every endpoint requires a valid Keycloak Bearer token. The routes under /energy_management and /flexibility_zones only depend on get_current_user: they do not check community membership or role. The legacy /setpoints routes additionally apply the community permission check (is_allowed): platform admins are unrestricted, community admins have full access in their community, and community members may only use safe (read) methods.

Conventions. Dated configuration changes follow Business Validity (start_date / end_date inclusive, Europe/Lisbon). Timestamps are ISO 8601 and stored with time zone. External-push endpoints (/setpoints/external, /offers/external, /member_energy/external, /baseline/external) resolve the target configuration as the active configuration of that module_type in the community, so create and activate a configuration first (404 otherwise).

Endpoint summary

Method Path Purpose
GET /core/api/communities/{community_id}/energy_management/modules List all registered EMS modules
GET /core/api/communities/{community_id}/energy_management/modules/{module_type} Get one module's metadata
GET /core/api/communities/{community_id}/energy_management/modules/{module_type}/schema Get a module's config_data JSON schema
POST /core/api/communities/{community_id}/energy_management/configs Create (activate) a module configuration
GET /core/api/communities/{community_id}/energy_management/configs List the community's configurations
GET /core/api/communities/{community_id}/energy_management/configs/{config_id} Get one configuration
PATCH /core/api/communities/{community_id}/energy_management/configs/{config_id} Update a configuration from a date
DELETE /core/api/communities/{community_id}/energy_management/configs/{config_id} Remove a configuration's validity period
POST /core/api/communities/{community_id}/energy_management/configs/{config_id}/calculate Trigger a calculation
POST /core/api/communities/{community_id}/energy_management/baseline/external/{module_type}/batch Push per-CPE baseline values
GET /core/api/communities/{community_id}/energy_management/baseline Query baseline values
GET /core/api/communities/{community_id}/energy_management/setpoints Query setpoints (paginated)
GET /core/api/communities/{community_id}/energy_management/setpoints/export Export setpoints as CSV
DELETE /core/api/communities/{community_id}/energy_management/setpoints Delete setpoints and member energy data
POST /core/api/communities/{community_id}/energy_management/setpoints/external/{module_type}/batch Push external setpoints (batch)
POST /core/api/communities/{community_id}/energy_management/setpoints/external Push external setpoints (module in query)
POST /core/api/communities/{community_id}/energy_management/offers/external/{module_type} Push one external flexibility offer
POST /core/api/communities/{community_id}/energy_management/offers/external/{module_type}/batch Push several external flexibility offers
GET /core/api/communities/{community_id}/energy_management/offers List flexibility offers
GET /core/api/communities/{community_id}/energy_management/offers/{offer_id} Get one flexibility offer
PATCH /core/api/communities/{community_id}/energy_management/offers/{offer_id} Update a flexibility offer
POST /core/api/communities/{community_id}/energy_management/member_energy/external/{module_type}/batch Push CPE-level energy data
GET /core/api/communities/{community_id}/energy_management/member_energy Query CPE-level energy data
GET /core/api/flexibility_zones List flexibility zones
POST /core/api/flexibility_zones Create a flexibility zone
GET /core/api/flexibility_zones/{zone_id} Get a flexibility zone
PATCH /core/api/flexibility_zones/{zone_id} Update a flexibility zone
DELETE /core/api/flexibility_zones/{zone_id} Delete a flexibility zone
GET /core/api/communities/{community_id}/setpoints/pagination Legacy: paginated battery setpoints
POST /core/api/communities/{community_id}/setpoints/api_upload Legacy: upload battery setpoints

Modules

Module discovery. These three routes do not read the community; community_id is only part of the path.

List Modules

GET /core/api/communities/{community_id}/energy_management/modules

Returns every registered EMS module with its metadata (category, maturity, capabilities). Use it to find which module_type values can be configured and what each can do.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community (not validated)

Example

curl https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/energy_management/modules \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "modules": [
    {
      "module_type": "GDBN_BEFLEXIBLE",
      "display_name": "GDBN BeFlexible",
      "description": "Integration with GDBN flexibility market platform",
      "version": "1.0.1",
      "capabilities": ["external_setpoint_reception", "external_offer_reception", "offer_storage"],
      "requires_external_api": false,
      "icon": "cloud_queue",
      "category": "FLEXIBILITY",
      "maturity": "DEVELOPMENT"
    }
  ],
  "total": 7
}

Tip

category is one of OPTIMIZATION, MARKET_INTEGRATION, FLEXIBILITY; maturity is one of PRODUCTION, BETA, DEVELOPMENT (see Optimization). Only modules whose capabilities include external_setpoint_reception accept /setpoints/external pushes.

Get Module Metadata

GET /core/api/communities/{community_id}/energy_management/modules/{module_type}

Returns the metadata of a single module (same object as an element of the list above).

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community (not validated)
module_type path string Yes e.g. OSTEC_SIMPLE, GDBN_BEFLEXIBLE

Example

curl https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/energy_management/modules/OSTEC_SIMPLE \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "module_type": "OSTEC_SIMPLE",
  "display_name": "OSTEC Simple",
  "description": "Optimized OSTEC Simple energy management with collective pool MILP",
  "version": "2.0.0",
  "capabilities": ["local_optimization", "setpoint_calculation", "storage_management", "ev_management"],
  "category": "OPTIMIZATION",
  "maturity": "PRODUCTION"
}

Tip

Returns 404 (Module type '...' not found) for an unknown module_type. The match is exact and case-sensitive.

Get Module Config Schema

GET /core/api/communities/{community_id}/energy_management/modules/{module_type}/schema

Returns the JSON Schema describing the module's config_data. Call it before creating a configuration to know the accepted keys, defaults and enums.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community (not validated)
module_type path string Yes Module identifier

Example

curl https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/energy_management/modules/OSTEC_SIMPLE/schema \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "module_type": "OSTEC_SIMPLE",
  "config_schema": {
    "type": "object",
    "properties": {
      "l_extra": { "type": "number", "default": 10.0 },
      "l_market_buy": { "type": "number", "default": 500.0 },
      "l_market_sell": { "type": "number", "default": -500.0 },
      "solver": { "type": "string" }
    }
  }
}

Tip

404 for an unknown module. The schema is advisory: the authoritative check is the module's own validate_config, run when a configuration is created or its config_data is patched.


Configurations

Creating a configuration activates a module for a community. A community has at most one configuration per module_type. Configuration fields other than module_type are dated (see Business Validity); every dated version is listed by GET /core/api/communities/{community_id}/business-validity/ems/{config_id}/versions.

Create Configuration

POST /core/api/communities/{community_id}/energy_management/configs

Creates the configuration and its first dated version (is_active = true) and invalidates calculated data for the covered period.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type body string Yes See Optimization for the full list
start_date body date Yes First day (inclusive, Europe/Lisbon) on which this configuration applies
end_date body date No Last day (inclusive). null = open-ended
horizon body integer No Optimization window in hours. Default 24
delta_t body number No Time step in hours (0.25 = 15 min). Default 1.0
strict_pos_coeffs body boolean No Enforce strictly positive allocation coefficients. Default true
total_share_coeffs body boolean No Use total share coefficients. Default false
config_data body object No Module-specific parameters (validated by the module). Default {}
curl -X POST \
  https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/energy_management/configs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "module_type": "OSTEC_SIMPLE",
    "start_date": "2026-01-01",
    "horizon": 24,
    "delta_t": 1.0,
    "strict_pos_coeffs": true,
    "total_share_coeffs": false,
    "config_data": {
      "solver": "CPLEX",
      "l_extra": 10.0,
      "l_market_buy": 500.0,
      "l_market_sell": -500.0
    }
  }'
curl -X POST \
  https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/energy_management/configs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "module_type": "OSTEC_PROMOTOR",
    "start_date": "2026-01-01",
    "horizon": 24,
    "delta_t": 1.0,
    "config_data": {
      "type_coef": "PROP_AC",
      "type_method": "BILEVEL_KKT_QUADCOST",
      "type_solver": "CBC",
      "percentage": 1.0
    }
  }'
curl -X POST \
  https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/energy_management/configs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "module_type": "GDBN_BEFLEXIBLE",
    "start_date": "2026-01-01",
    "horizon": 24,
    "delta_t": 1.0,
    "config_data": {
      "zone_name": "Lisbon Zone 1",
      "l_flex_init": 0.01,
      "l_flex_increment": 0.01,
      "rho_tot": 1.0,
      "divisible": true
    }
  }'

Response (200)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
  "module_type": "OSTEC_SIMPLE",
  "horizon": 24,
  "delta_t": 1.0,
  "strict_pos_coeffs": true,
  "total_share_coeffs": false,
  "config_data": { "solver": "CPLEX", "l_extra": 10.0 },
  "is_active": true,
  "created_at": "2026-04-17T10:00:00Z",
  "updated_at": "2026-04-17T10:00:00Z",
  "last_run_at": null,
  "last_run_status": null,
  "last_error": null,
  "run_count": 0,
  "version_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "start_date": "2026-01-01",
  "end_date": null
}

Tip

Save the id: it is needed to trigger calculations. Errors: 404 community not found, 400 unknown module_type or Invalid configuration: ... from the module's validation, 409 if the community already has a configuration for that module_type (change it with PATCH instead). config_data keys differ per module; the keys above come from each module's schema (for example, OSTEC_SIMPLE.solver must be CPLEX, CBC or GLPK; OSTEC_PROMOTOR.type_coef one of TREE_AC, PRICE_AC, PERC_AC, PROP_AC, BEST_AC). See OSTEC Simple, OSTEC Promotor, GDBN BeFlexible.

List Configurations

GET /core/api/communities/{community_id}/energy_management/configs

Lists all configurations of the community, one per activated module.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community

Example

curl https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/configs \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "module_type": "OSTEC_SIMPLE",
    "is_active": true,
    "last_run_status": "SUCCESS",
    "run_count": 12,
    "version_id": null,
    "start_date": null,
    "end_date": null
  }
]

Tip

404 if the community does not exist. This reads the configuration row itself, not a dated version: version_id, start_date and end_date are null here. Use the business-validity .../ems/{config_id}/versions endpoint for the dated history.

Get Configuration

GET /core/api/communities/{community_id}/energy_management/configs/{config_id}

Returns one configuration, including the run status fields used to follow asynchronous calculations.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
config_id path UUID Yes Configuration ID

Example

curl https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/configs/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "module_type": "OSTEC_PROMOTOR",
  "is_active": true,
  "last_run_at": "2026-04-17T10:05:00Z",
  "last_run_status": "RUNNING",
  "last_error": null,
  "run_count": 3
}

Tip

404 (Configuration not found) if the ID does not belong to this community. last_run_status is RUNNING, SUCCESS or FAILED; on FAILED read last_error. As with the list, dated-version fields are null.

Update Configuration

PATCH /core/api/communities/{community_id}/energy_management/configs/{config_id}

Changes the configuration from start_date on (or for [start_date, end_date]). Earlier periods keep their values. Send only the fields to change.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
config_id path UUID Yes Configuration ID
start_date body date Yes First day the change applies (inclusive)
end_date body date No Last day the change applies (inclusive). null = open-ended
horizon body integer No Optimization window in hours
delta_t body number No Time step in hours
strict_pos_coeffs body boolean No Strictly positive coefficients
total_share_coeffs body boolean No Total share coefficients
config_data body object No Replaces the whole config_data (validated by the module)
is_active body boolean No Enable or disable the module for the period

Example (disable the module from 1 May)

curl -X PATCH \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/configs/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false, "start_date": "2026-05-01" }'

Response (abridged)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "module_type": "OSTEC_SIMPLE",
  "is_active": false,
  "version_id": "0d9c3b7a-52f1-4e6b-9a38-6f1b0c2d4e55",
  "start_date": "2026-05-01",
  "end_date": null
}

Tip

module_type cannot be changed. config_data is replaced, not merged: send the full object. Fields you omit keep the values of the version effective on start_date. Errors: 404 configuration not found or no configuration version effective on start_date; 400 invalid config_data; 422 if start_date is missing. The non-dated configuration row (returned by the GET endpoints) is updated with the patched values regardless of start_date, so its is_active reflects the latest patch, not necessarily today's state. Calculated data in the affected period is invalidated.

Delete Configuration Validity

DELETE /core/api/communities/{community_id}/energy_management/configs/{config_id}

Removes the configuration from effective_at on, or only for [effective_at, end_date] when end_date is given.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
config_id path UUID Yes Configuration ID
effective_at query date Yes First day to remove (inclusive)
end_date query date No Last day to remove (inclusive)

Example

curl -X DELETE \
  "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/configs/3fa85f64-5717-4562-b3fc-2c963f66afa6?effective_at=2026-05-01" \
  -H "Authorization: Bearer $TOKEN"

Response

{
  "message": "Configuration validity removed successfully",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

Tip

This is a dated removal, not a hard delete: the handler only calls the validity-period removal for the configuration's versions. 404 if the configuration is not in this community; 422 if effective_at is missing. Calculated data in the period is invalidated. To disable a module without removing its history, use PATCH with is_active: false.

Trigger Calculation

POST /core/api/communities/{community_id}/energy_management/configs/{config_id}/calculate

Runs the module's optimization for a day-ahead window starting at midnight (Europe/Lisbon) on target_date and lasting horizon hours. OSTEC_PROMOTOR runs asynchronously in a Celery worker; other modules run inside the request.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
config_id path UUID Yes Configuration ID
target_date query date (YYYY-MM-DD) No Day-ahead date. Default: tomorrow (server date + 1)

Example

curl -X POST \
  "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/configs/3fa85f64-5717-4562-b3fc-2c963f66afa6/calculate?target_date=2026-04-18" \
  -H "Authorization: Bearer $TOKEN"

Response (synchronous modules)

{
  "message": "Calculation completed successfully",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "module_type": "OSTEC_SIMPLE",
  "result_summary": { "status": "success", "setpoints_count": 96 }
}

Response (OSTEC_PROMOTOR, asynchronous)

{
  "message": "Calculation dispatched to background worker",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "module_type": "OSTEC_PROMOTOR",
  "target_date": "2026-04-18",
  "async": true
}

Tip

The configuration version effective on target_date must exist (400 No EMS configuration is effective on ...) and be active (400 Configuration is not active). If the horizon (ceil(horizon / 24) days from target_date) crosses a day on which the configuration or community structure changes, the call is rejected with 409; run it on each side of that day (see Business Validity). For async runs poll GET .../configs/{config_id}: last_run_status goes RUNNING then SUCCESS or FAILED (see last_error). For synchronous runs, if no load forecasts exist the call returns 200 with {"message": "No forecasts available for calculation", "config_id": ...} and sets last_run_status = SUCCESS; an exception during the run returns 500 Calculation failed: ... and sets FAILED. Modules that receive their results externally (GDBN_BEFLEXIBLE) do not compute setpoints locally.


Baseline

Per-CPE baseline: the expected net consumption without flexibility, computed by an external system.

Push Baseline Values (Batch)

POST /core/api/communities/{community_id}/energy_management/baseline/external/{module_type}/batch

Stores baseline values, upserting on (config_id, cpe_id, datetime_timestamp).

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type path string Yes Module whose active configuration receives the data
items body array Yes List of baseline records
items[].cpe_id body UUID Yes CPE meter ID
items[].datetime_timestamp body datetime Yes Timestamp of the value
items[].baseline body number Yes Baseline value
items[].metadata body object No Free-form metadata. Default {}

Example

curl -X POST \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/baseline/external/GDBN_BEFLEXIBLE/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "cpe_id": "550e8400-e29b-41d4-a716-446655440000",
        "datetime_timestamp": "2026-04-18T10:00:00Z",
        "baseline": 1.4,
        "metadata": { "source": "GDBN_BEFLEXIBLE" }
      }
    ]
  }'

Response (201)

{ "stored": 1, "module_type": "GDBN_BEFLEXIBLE" }

Tip

Errors: 404 community not found or no active configuration for module_type, 400 unknown module type, 500 on a database failure (the whole batch is rolled back). No capability check is applied. Unit is not enforced by the API (the sibling member-energy data is in kWh).

Get Baseline Values

GET /core/api/communities/{community_id}/energy_management/baseline

Returns stored baseline records, newest first.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Module type
cpe_id query UUID No Filter by CPE
start_datetime query datetime No Inclusive lower bound
end_datetime query datetime No Inclusive upper bound
limit query integer No Page size. Default 500
offset query integer No Rows to skip. Default 0

Example

curl "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/baseline?module_type=GDBN_BEFLEXIBLE&start_datetime=2026-04-18T00:00:00Z&end_datetime=2026-04-18T23:59:59Z" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "a1b2c3d4-0000-4000-8000-000000000001",
    "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
    "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "module_type": "GDBN_BEFLEXIBLE",
    "cpe_id": "550e8400-e29b-41d4-a716-446655440000",
    "datetime_timestamp": "2026-04-18T10:00:00Z",
    "baseline": 1.4,
    "calculation_metadata": {},
    "created_at": "2026-04-17T10:00:00Z"
  }
]

Tip

The response is a plain array (no total count). 404 if the community does not exist; module_type is required (422 otherwise).


Setpoints

Setpoints are produced by the module (or pushed by an external service) per resource or meter and timestamp. setpoint_type is CONSUME (consume/charge) or INJECT (inject/discharge); the API stores the string as sent and does not validate or translate it.

Query Setpoints

GET /core/api/communities/{community_id}/energy_management/setpoints

Returns paginated setpoints, newest first, enriched with resource and meter names.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Module type
start_datetime query datetime No Inclusive lower bound
end_datetime query datetime No Inclusive upper bound
resource_id query UUID No Filter by resource
limit query integer No Page size. Default 100
offset query integer No Rows to skip. Default 0

Example

curl "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/setpoints?module_type=OSTEC_SIMPLE&start_datetime=2026-04-18T00:00:00Z&end_datetime=2026-04-18T23:59:59Z&limit=100&offset=0" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "data": [
    {
      "id": "b7e0f5a2-1c3d-4e8f-9a6b-2d4c6e8f0a1b",
      "datetime_timestamp": "2026-04-18T10:00:00Z",
      "setpoint_type": "CONSUME",
      "setpoint_value": 2500.0,
      "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "meter_id": "660e8400-e29b-41d4-a716-446655440001",
      "resource_name": "Battery 1",
      "resource_type": "STORAGE",
      "meter_name": "PT0002000000000000XX",
      "metadata": {}
    }
  ],
  "total_count": 96,
  "limit": 100,
  "offset": 0
}

Tip

404 if the community does not exist. Rows without a resource but with metadata.source = "e_cmet" are labelled "Net schedule"; rows with only a meter use the meter's CPE/identifier as resource_name. Soft-deleted resources are still resolved to a name. The value is not unit-converted by the API; the schema describes it as power (for example Watts).

Export Setpoints (CSV)

GET /core/api/communities/{community_id}/energy_management/setpoints/export

Downloads all setpoints of a module in the period as a CSV file, oldest first.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Module type
start_datetime query datetime No Inclusive lower bound
end_datetime query datetime No Inclusive upper bound

Example

curl -o setpoints.csv \
  "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/setpoints/export?module_type=OSTEC_SIMPLE&start_datetime=2026-04-18T00:00:00Z&end_datetime=2026-04-18T23:59:59Z" \
  -H "Authorization: Bearer $TOKEN"

Response (text/csv, abridged)

datetime_timestamp,module_type,setpoint_type,setpoint_value,resource_id,resource_name,meter_id,meter_name
2026-04-18T10:00:00+00:00,OSTEC_SIMPLE,CONSUME,2500.0,3fa85f64-5717-4562-b3fc-2c963f66afa6,Battery 1,660e8400-e29b-41d4-a716-446655440001,PT0002000000000000XX

Tip

Served as an attachment named setpoints_{module_type}_{start_date|all}_{end_date|all}.csv. No pagination: the whole range is returned. 404 if the community does not exist.

Delete Setpoints

DELETE /core/api/communities/{community_id}/energy_management/setpoints

Deletes the setpoints of a module, optionally limited to a time range.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Module type
start_datetime query datetime No Inclusive lower bound
end_datetime query datetime No Inclusive upper bound

Example

curl -X DELETE \
  "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/setpoints?module_type=OSTEC_SIMPLE&start_datetime=2026-04-18T00:00:00Z&end_datetime=2026-04-18T23:59:59Z" \
  -H "Authorization: Bearer $TOKEN"

Response

{ "message": "Setpoints and member energy data deleted successfully" }

Warning

With no start_datetime/end_datetime, all setpoints of the module in the community are deleted. The same time range is also applied to the module's member energy data (/member_energy), which is deleted as well; baseline data and offers are not touched. 404 if the community does not exist.

Push External Setpoints (Batch)

POST /core/api/communities/{community_id}/energy_management/setpoints/external/{module_type}/batch

Lets an external service (for example GDBN) push calculated setpoints. The module must declare the external_setpoint_reception capability (GDBN_BEFLEXIBLE, OSTEC_FLEX_HEMS). The body is a JSON array.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type path string Yes Receiving module
[].resource_id body UUID No Resource (battery, EV, ...)
[].meter_id body UUID No Associated meter
[].datetime_timestamp body datetime Yes Setpoint timestamp
[].setpoint_value body number Yes Setpoint value
[].setpoint_type body string Yes CONSUME or INJECT
[].metadata body object No Free-form metadata. Default {}

Example

curl -X POST \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/setpoints/external/GDBN_BEFLEXIBLE/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "meter_id": "660e8400-e29b-41d4-a716-446655440001",
      "datetime_timestamp": "2026-04-18T10:00:00Z",
      "setpoint_value": 5.2,
      "setpoint_type": "CONSUME",
      "metadata": { "source": "GDBN_BEFLEXIBLE" }
    }
  ]'

Response (201)

{
  "status": "success",
  "module_type": "GDBN_BEFLEXIBLE",
  "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "stored_count": 1,
  "message": "Successfully stored 1 setpoints from external GDBN_BEFLEXIBLE service"
}

Tip

Upsert key: (config_id, resource_id, datetime_timestamp, setpoint_type); on conflict the setpoint_value and metadata are overwritten. Errors: 404 community not found or no active configuration, 400 unknown module, 403 module lacks external_setpoint_reception, 500 on database failure (batch rolled back). See GDBN BeFlexible.

Push External Setpoints (Module in Query)

POST /core/api/communities/{community_id}/energy_management/setpoints/external

Same behaviour, validation and response as the batch endpoint above, but module_type is a query parameter and there is no path segment.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Receiving module
[] body array Yes Same items as the batch endpoint

Example

curl -X POST \
  "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/setpoints/external?module_type=GDBN_BEFLEXIBLE" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"datetime_timestamp": "2026-04-18T10:00:00Z", "setpoint_value": 5.2, "setpoint_type": "INJECT"}]'

Response (201)

{
  "status": "success",
  "module_type": "GDBN_BEFLEXIBLE",
  "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "stored_count": 1,
  "message": "Successfully stored 1 setpoints from external GDBN_BEFLEXIBLE service"
}

Tip

Despite the name, this endpoint also takes a list. Prefer the /{module_type}/batch form; this one exists as an alias with the module in the query string.


Offers

Flexibility offers (bids) for flexibility modules. Only GDBN_BEFLEXIBLE implements the storage of external offers. Offers are a read/update resource here; creation happens through the external endpoints.

Push External Offer

POST /core/api/communities/{community_id}/energy_management/offers/external/{module_type}

Stores one offer submitted by the external client after it was placed in GDBN. The module must declare external_offer_reception.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type path string Yes Receiving module, e.g. GDBN_BEFLEXIBLE
id body UUID Yes Bid ID from the external system (stored as external_offer_id)
start_datetime body datetime Yes Start of provision window (alias startDatetime)
end_datetime body datetime Yes End of provision window (alias endDatetime)
direction body string Yes UP or DOWN (case-insensitive)
quantity body number Yes Quantity (MWh)
price body number Yes Activation price (EUR/MWh)
divisible body boolean No Default true
recovery_time body number No Recovery time in hours (alias recoveryTime)
status body string No PLACED (default), ACCEPTED, REJECTED, ACTIVE
flex_needs_id body UUID No Flexibility need ID (alias flexNeedsId)

Example

curl -X POST \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/offers/external/GDBN_BEFLEXIBLE \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "start_datetime": "2026-04-18T15:00:00Z",
    "end_datetime": "2026-04-18T16:00:00Z",
    "direction": "UP",
    "quantity": 2.5,
    "price": 45.0,
    "divisible": true,
    "recovery_time": 1.5,
    "status": "PLACED",
    "flex_needs_id": "660e8400-e29b-41d4-a716-446655440001"
  }'

Response (201)

{
  "id": "c3d4e5f6-0000-4000-8000-000000000002",
  "external_offer_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "stored",
  "message": "Offer stored successfully from external GDBN service"
}

Tip

Idempotent on id: if an offer with that external ID already exists it is not updated and the response carries "status": "skipped" (use PATCH /offers/{offer_id} to change it). recovery_time is stored truncated to an integer (the original is kept in offer_data). Errors: 404 community or active configuration not found, 400 unknown module, 403 module lacks external_offer_reception, 422 invalid direction, 500 on storage failure.

Push External Offers (Batch)

POST /core/api/communities/{community_id}/energy_management/offers/external/{module_type}/batch

Stores several offers in one call. Each offer is processed independently.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type path string Yes Receiving module
offers body array Yes List of offers, same fields as the single endpoint

Example

curl -X POST \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/offers/external/GDBN_BEFLEXIBLE/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "offers": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "start_datetime": "2026-04-18T15:00:00Z",
        "end_datetime": "2026-04-18T16:00:00Z",
        "direction": "DOWN",
        "quantity": 1.0,
        "price": 30.0
      }
    ]
  }'

Response (201, abridged)

{
  "status": "success",
  "module_type": "GDBN_BEFLEXIBLE",
  "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "stored_count": 1,
  "failed_count": 0,
  "stored_offers": [
    { "id": "c3d4e5f6-0000-4000-8000-000000000002", "external_offer_id": "550e8400-e29b-41d4-a716-446655440000", "status": "stored", "message": "Offer stored successfully from external GDBN service" }
  ],
  "failed_offers": [],
  "message": "Successfully stored 1 offers, 0 failed"
}

Tip

status is partial when some offers failed (failed_offers lists external_offer_id and error); the HTTP code is still 201. Duplicates are counted in stored_count with "status": "skipped" in stored_offers. Unlike the single endpoint, the batch does not check the external_offer_reception capability. 404 community or active configuration not found, 400 unknown module.

List Offers

GET /core/api/communities/{community_id}/energy_management/offers

Lists the flexibility offers of a module, optionally filtered by window and status.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Module type
start_datetime query datetime No Offers starting at or after this instant
end_datetime query datetime No Offers ending at or before this instant
status query string No Exact status match, e.g. PLACED

Example

curl "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/offers?module_type=GDBN_BEFLEXIBLE&status=PLACED" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "c3d4e5f6-0000-4000-8000-000000000002",
    "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
    "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "module_type": "GDBN_BEFLEXIBLE",
    "external_offer_id": "550e8400-e29b-41d4-a716-446655440000",
    "flexibility_zone_id": null,
    "flexibility_needs_id": "660e8400-e29b-41d4-a716-446655440001",
    "direction": "UP",
    "quantity": 2.5,
    "divisible": true,
    "max_number_activations": null,
    "activation_price": 45.0,
    "reservation_price": null,
    "recovery_time": 1,
    "start_datetime": "2026-04-18T15:00:00Z",
    "end_datetime": "2026-04-18T16:00:00Z",
    "status": "PLACED",
    "offer_data": {},
    "created_at": "2026-04-17T10:00:00Z",
    "updated_at": "2026-04-17T10:00:00Z"
  }
]

Tip

No pagination or explicit ordering. 404 if the community does not exist. For GDBN offers flexibility_zone_id is always null (the zone is managed in GDBN, not in the internal flexibility zones table).

Get Offer

GET /core/api/communities/{community_id}/energy_management/offers/{offer_id}

Returns one offer.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
offer_id path UUID Yes Internal offer ID or the external offer ID

Example

curl https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/offers/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $TOKEN"

Response

Same object as an element of the list response above.

Tip

offer_id matches either id or external_offer_id, so the GDBN bid ID can be used directly. 404 (Flexibility offer not found) if no offer matches in this community.

Update Offer

PATCH /core/api/communities/{community_id}/energy_management/offers/{offer_id}

Updates an offer, typically to change its status after a GDBN webhook.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
offer_id path UUID Yes Internal or external offer ID
status body string No PLACED, ACCEPTED, REJECTED, ACTIVE
offer_data body object No Replaces the offer's offer_data JSON

Example

curl -X PATCH \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/offers/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "ACCEPTED" }'

Response

The updated offer (same shape as in List Offers) with "status": "ACCEPTED".

Tip

The listed statuses are documented in the schema, but the server does not validate the value: any string is stored. Only the fields sent are changed. 404 if not found, 500 on storage failure.


Member Energy

CPE-level (grid connection point) energy flows produced by an optimization or external service, in kWh. Not per resource.

Push Member Energy (Batch)

POST /core/api/communities/{community_id}/energy_management/member_energy/external/{module_type}/batch

Stores CPE-level data, upserting on (config_id, cpe_id, datetime_timestamp). The body is a JSON array.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type path string Yes Module whose active configuration receives the data
[].cpe_id body UUID Yes CPE meter ID
[].datetime_timestamp body datetime Yes Timestamp
[].e_pur body number No Energy purchased in the local market (kWh)
[].e_sale body number No Energy sold in the local market (kWh)
[].e_sup body number No Energy purchased from the retailer (kWh)
[].e_sur body number No Energy sold to the retailer (kWh)
[].e_cmet body number No Net consumption at the CPE meter (kWh)
[].metadata body object No Free-form metadata. Default {}

Example

curl -X POST \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/member_energy/external/GDBN_BEFLEXIBLE/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "cpe_id": "550e8400-e29b-41d4-a716-446655440000",
      "datetime_timestamp": "2026-04-18T15:00:00Z",
      "e_pur": 0.5,
      "e_sale": 0.2,
      "e_sup": 1.1,
      "e_sur": 0.0,
      "e_cmet": 1.4,
      "metadata": { "source": "GDBN_BEFLEXIBLE" }
    }
  ]'

Response (201)

{
  "status": "success",
  "module_type": "GDBN_BEFLEXIBLE",
  "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
  "config_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "stored_count": 1,
  "message": "Successfully stored 1 member energy records"
}

Tip

All energy fields are optional, so partial records can be stored; on conflict every field is overwritten with the incoming value (an omitted field becomes null). Errors: 404 community or active configuration not found, 400 unknown module, 500 on database failure (rolled back). No capability check is applied. Deleting setpoints also deletes member energy in the same window.

Get Member Energy

GET /core/api/communities/{community_id}/energy_management/member_energy

Returns stored CPE-level data, newest first.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
community_id path UUID Yes Community
module_type query string Yes Module type
cpe_id query UUID No Filter by CPE
start_datetime query datetime No Inclusive lower bound
end_datetime query datetime No Inclusive upper bound
limit query integer No Page size. Default 500
offset query integer No Rows to skip. Default 0

Example

curl "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/energy_management/member_energy?module_type=GDBN_BEFLEXIBLE&cpe_id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "d4e5f6a7-0000-4000-8000-000000000003",
    "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21",
    "module_type": "GDBN_BEFLEXIBLE",
    "cpe_id": "550e8400-e29b-41d4-a716-446655440000",
    "datetime_timestamp": "2026-04-18T15:00:00Z",
    "e_pur": 0.5,
    "e_sale": 0.2,
    "e_sup": 1.1,
    "e_sur": 0.0,
    "e_cmet": 1.4,
    "metadata": { "source": "GDBN_BEFLEXIBLE" },
    "created_at": "2026-04-17T10:00:00Z"
  }
]

Tip

Plain array without a total count. 404 if the community does not exist. The JSON key is metadata (stored as calculation_metadata).


Flexibility Zones

Internal flexibility zones, shared by all communities (not community-scoped). Base path /core/api/flexibility_zones. GDBN_BEFLEXIBLE does not use this table: its zone lives in GDBN.

List Flexibility Zones

GET /core/api/flexibility_zones

Lists every flexibility zone.

Auth / permissions: any authenticated user.

Example

curl https://<host>/core/api/flexibility_zones -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "e5f6a7b8-0000-4000-8000-000000000004",
    "name": "Lisbon Flexibility Zone 1",
    "description": "Primary flexibility zone for Lisbon region",
    "zone_type": "GDBN_BEFLEXIBLE",
    "zone_metadata": { "region": "Lisbon", "capacity_mw": 10.0 },
    "created_at": "2026-04-17T10:00:00Z",
    "updated_at": "2026-04-17T10:00:00Z"
  }
]

Tip

No filters or pagination; the list is global across communities.

Create Flexibility Zone

POST /core/api/flexibility_zones

Creates a zone.

Auth / permissions: any authenticated user (no role check).

Parameter In Type Required Description
name body string Yes 1 to 255 characters, unique
description body string No Free text
zone_type body string No Default CUSTOM (e.g. GDBN_BEFLEXIBLE)
zone_metadata body object No Zone-specific JSON. Default {}

Example

curl -X POST https://<host>/core/api/flexibility_zones \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Lisbon Flexibility Zone 1",
    "description": "Primary flexibility zone for Lisbon region",
    "zone_type": "GDBN_BEFLEXIBLE",
    "zone_metadata": { "region": "Lisbon", "capacity_mw": 10.0 }
  }'

Response (201)

Same object as in the list response above.

Tip

409 if a zone with the same name exists; 422 if name is empty or longer than 255.

Get Flexibility Zone

GET /core/api/flexibility_zones/{zone_id}

Returns one zone.

Auth / permissions: any authenticated user.

Parameter In Type Required Description
zone_id path UUID Yes Zone ID

Example

curl https://<host>/core/api/flexibility_zones/e5f6a7b8-0000-4000-8000-000000000004 \
  -H "Authorization: Bearer $TOKEN"

Response

Same object as an element of the list response.

Tip

404 (Flexibility zone not found) for an unknown ID.

Update Flexibility Zone

PATCH /core/api/flexibility_zones/{zone_id}

Partially updates a zone; only the fields sent are changed.

Auth / permissions: any authenticated user (no role check).

Parameter In Type Required Description
zone_id path UUID Yes Zone ID
name body string No 1 to 255 characters, unique
description body string No Free text
zone_type body string No Zone type
zone_metadata body object No Replaces the metadata object

Example

curl -X PATCH https://<host>/core/api/flexibility_zones/e5f6a7b8-0000-4000-8000-000000000004 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "description": "Updated description", "zone_metadata": { "capacity_mw": 12.5 } }'

Response

The updated zone (same shape as in the list response).

Tip

404 unknown zone; 409 if the new name is already used by another zone. zone_metadata is replaced, not merged.

Delete Flexibility Zone

DELETE /core/api/flexibility_zones/{zone_id}

Deletes a zone.

Auth / permissions: any authenticated user (no role check).

Parameter In Type Required Description
zone_id path UUID Yes Zone ID

Example

curl -X DELETE https://<host>/core/api/flexibility_zones/e5f6a7b8-0000-4000-8000-000000000004 \
  -H "Authorization: Bearer $TOKEN"

Response

{
  "message": "Flexibility zone deleted successfully",
  "zone_id": "e5f6a7b8-0000-4000-8000-000000000004"
}

Tip

404 unknown zone. The zone is shared across communities, so deleting it affects every community that references it; a database failure (for example, a constraint violation) returns 500.


Legacy Setpoints

Legacy

These routes read and write the older battery setpoint table (cmd and set_point per resource), not the unified energy_setpoints table used by /energy_management/setpoints. Use the unified endpoints for new integrations.

Get Battery Setpoints (Paginated)

GET /core/api/communities/{community_id}/setpoints/pagination

Returns battery setpoints of all the community's resources with filtering and sorting, for table views.

Auth / permissions: community member or admin of community_id, or platform admin (is_allowed).

Parameter In Type Required Description
community_id path UUID Yes Community
limit query integer Yes Page size
offset query integer Yes Rows to skip
date query string (YYYY-MM-DD) No Filter by calendar date of the timestamp
time query string (HH:MM:SS) No Filter by time of day
resource_id query UUID No Filter by resource
sort_column query string No date, time, resource_id (sorts by resource name), or a setpoint column such as datetime_timestamp, cmd, set_point
sort_direction query string No asc; anything else sorts descending

Example

curl "https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/setpoints/pagination?limit=50&offset=0&date=2026-04-18&sort_column=time&sort_direction=asc" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 96,
  "data": [
    {
      "datetime_timestamp": "2026-04-18T10:00:00Z",
      "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "cmd": "CHA",
      "set_point": 2500.0,
      "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21"
    }
  ],
  "distinct_dates": [{ "date": "2026-04-18" }],
  "distinct_times": [{ "time": "10:00:00" }]
}

Tip

cmd is OFF, CHA (charging) or DIS (discharging); set_point is in Watts. distinct_dates and distinct_times list the values available under the active filters, to populate filter dropdowns. limit and offset are required (422 if missing). 403 for non-members.

Upload Battery Setpoints

POST /core/api/communities/{community_id}/setpoints/api_upload

Uploads a batch of battery setpoints. Existing setpoints with the same (datetime_timestamp, resource_id) are deleted and replaced.

Auth / permissions: community admin of community_id or platform admin (is_allowed rejects writes from plain members).

Parameter In Type Required Description
community_id path UUID Yes Community
[].datetime_timestamp body datetime Yes Setpoint timestamp
[].resource_id body UUID Yes Resource
[].cmd body string Yes OFF, CHA or DIS
[].set_point body number Yes Setpoint (Watts)
[].community_id body UUID Yes Community of the setpoint

Example

curl -X POST \
  https://<host>/core/api/communities/9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21/setpoints/api_upload \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "datetime_timestamp": "2026-04-18T10:00:00Z",
      "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "cmd": "CHA",
      "set_point": 2500.0,
      "community_id": "9b2c6f0e-1d3a-4c55-8a49-0e4f1c6a7d21"
    }
  ]'

Response (201)

{ "message": "Successfully created 1 setpoints." }

Tip

An invalid cmd returns 400 with Invalid setpoint data at setpoint during <timestamp> for resource <id>. (nothing is stored). 404 if a body community_id or the resources are not found. The body community_id is not compared with the path community_id; send the same value in both. An empty list is rejected (the resource lookup raises 404 before the 400 No setpoints provided. branch is reached).