Skip to content

Meters, Contracts, Resources & Ownerships API

Base paths (all under /core/api/communities/{community_id}):

  • /meters and /meters/{meter_id}/contracts (meters and their commercial contracts)
  • /resources (generators, batteries, EVs, water heaters, installations)
  • /ownerships (member shares of a resource)
  • /resp (public-grid RESP relationships between CPE pairs)

Meters, contracts, resources, ownerships and RESP pairs are dated records. Every create and update takes a required start_date and an optional inclusive end_date, most reads accept effective_at, and deletes end the entity from a given day. See Business Validity. For the data model behind these endpoints see Meters, Contracts, Resources and Ownerships and Members. Enum values are listed in Enums & Constants. A full walkthrough that calls these endpoints in order is in the Promoter Community Guide.

Common rules

  • All endpoints need a bearer token of a verified user. Platform super-users may call everything. A community admin may call everything inside their community. A community member may only call GET endpoints, and on the endpoints marked "member-scoped" below they only see the meters, contracts and resources that are behind resources they own (shares valid on effective_at). An empty result is returned when they own nothing.
  • Dates are whole calendar days in Europe/Lisbon. effective_at defaults to today.
  • A path community_id that does not exist, and an unknown meter_id, resource_id, user_id or contract_id, are answered with 403 and a "... does not exist." detail (the platform's NotFound error uses status 403). A non-member of the community also gets 403.
  • Body validation errors return 422 with the Pydantic error list.
  • Structural edits invalidate the community's already-computed market sessions in the affected period, so they are recomputed with the new structure.

Endpoint summary

Method Path Purpose
POST /core/api/communities/{community_id}/meters Create a meter (and RESP pairs for a CPE)
GET /core/api/communities/{community_id}/meters List the meters of a community
GET /core/api/communities/{community_id}/meters/by-identifier Get a meter by its identifier code
GET /core/api/communities/{community_id}/meters/{meter_id} Get a meter by ID
PUT /core/api/communities/{community_id}/meters/{meter_id} Update a meter for a period
DELETE /core/api/communities/{community_id}/meters/{meter_id} End a meter from a date
POST /core/api/communities/{community_id}/meters/{meter_id}/contracts Create a contract
POST /core/api/communities/{community_id}/meters/{meter_id}/contracts/{contract_id}/clone Clone a contract
GET /core/api/communities/{community_id}/meters/{meter_id}/contracts List the contracts valid on a day
PUT /core/api/communities/{community_id}/meters/{meter_id}/contracts/{contract_id} Replace a contract for a period
DELETE /core/api/communities/{community_id}/meters/{meter_id}/contracts/{contract_id} Remove a contract from a date or for a period
POST /core/api/communities/{community_id}/resources Create a resource with type details and ownerships
GET /core/api/communities/{community_id}/resources/available-meters List meters free for a resource period
GET /core/api/communities/{community_id}/resources List the resources of a community
GET /core/api/communities/{community_id}/resources/by-name Get a resource by name
GET /core/api/communities/{community_id}/resources/meter/{meter_id} List the resources behind a meter
GET /core/api/communities/{community_id}/resources/{resource_id} Get a resource by ID
PUT /core/api/communities/{community_id}/resources/{resource_id} Update a resource for a period
DELETE /core/api/communities/{community_id}/resources/{resource_id} End a resource from a date
POST /core/api/communities/{community_id}/ownerships/{user_id}/{resource_id} Assign an ownership share
GET /core/api/communities/{community_id}/ownerships/{user_id} List a member's ownership shares
PUT /core/api/communities/{community_id}/ownerships/{user_id}/{resource_id} Change an ownership share for a period
DELETE /core/api/communities/{community_id}/ownerships/{user_id}/{resource_id} End an ownership share from a date
GET /core/api/communities/{community_id}/resp List the RESP pairs of a community
PUT /core/api/communities/{community_id}/resp/{resp_id} Activate or deactivate a RESP pair for a period

Meters

Base path: /core/api/communities/{community_id}/meters

Meter types are CPE, INTERNAL and AGGREGATOR_METER. Installation types are IC, IPR, IA and IAC; status is ACTIVE, INACTIVE or DELETED. See Meters.

Create a Meter

POST /core/api/communities/{community_id}/meters

Creates a meter with its first dated version. When the meter is a CPE, the platform also creates the RESP relationships (see RESP) between the new CPE and every other CPE valid on start_date.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id path UUID Yes Community
identifier body string Yes Meter code (for example PT0002...). Unique in the community, case-insensitive, for all time
type body enum Yes CPE, INTERNAL or AGGREGATOR_METER. Fixed after creation
installation_type body enum Yes IC, IPR, IA or IAC
community_id body UUID Yes Must match the path community
ord body string See tip DSO name, for example E-REDES. Required for CPE and AGGREGATOR_METER
production_identifier body string No Injection-point code. Mandatory in practice for IAC meters
status body enum No ACTIVE, INACTIVE, DELETED. Defaults to INACTIVE when omitted
cpe_id body UUID No Parent CPE. Only allowed for INTERNAL meters and must be a CPE of the same community
start_date body date Yes First day of validity (inclusive)
end_date body date No Last day of validity (inclusive). null = open-ended

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "PT0002000000000000000000001234",
    "type": "CPE",
    "installation_type": "IC",
    "ord": "E-REDES",
    "status": "ACTIVE",
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "start_date": "2026-01-01"
  }'

Response (abridged, 201 Created)

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "identifier": "PT0002000000000000000000001234",
  "type": "CPE",
  "ord": "E-REDES",
  "installation_type": "IC",
  "production_identifier": null,
  "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "ACTIVE",
  "cpe_id": null,
  "version_id": null,
  "start_date": null,
  "end_date": null
}

Tip

  • ord is required for CPE and AGGREGATOR_METER; without it the call fails with 400 "ORD is not valid for this community.". INTERNAL meters do not need it, and an ord sent with a non-CPE meter is discarded.
  • Only CPE meters generate RESP pairs (both directions, is_active: true, same start_date / end_date as the meter).
  • A duplicate identifier in the community returns 409. An invalid parent (cpe_id on a non-internal meter, or a parent that is not a CPE of this community) returns 422.
  • The create response is the stable meter record, so version_id, start_date and end_date are returned empty; read the meter with GET to get the dated version.

List Meters

GET /core/api/communities/{community_id}/meters

Lists the meters whose version is valid on effective_at, ordered by identifier.

Auth / permissions: any community member or admin (member-scoped: members only get meters behind resources they own).

Parameter In Type Required Description
community_id path UUID Yes Community
effective_at query date No Day to evaluate. Default: today (Europe/Lisbon)
exclude_deleted query string No true (default) hides meters with status DELETED. Send false to include them

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "identifier": "PT0002000000000000000000001234",
    "type": "CPE",
    "ord": "E-REDES",
    "installation_type": "IC",
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "ACTIVE",
    "cpe_id": null,
    "version_id": "9b2f1c1e-6d3a-4a55-8f7e-1d2c3b4a5f60",
    "start_date": "2026-01-01",
    "end_date": null
  }
]

Tip

A meter with no version covering effective_at (before its start_date, after its end_date, or in a gap) is not listed. The list is empty ([]), not an error, when nothing matches.

Get a Meter by Identifier

GET /core/api/communities/{community_id}/meters/by-identifier

Finds a meter by its identifier code (matched case-insensitively) inside the community.

Auth / permissions: any community member or admin (member-scoped: 403 when a member does not own a resource behind the meter).

Parameter In Type Required Description
community_id path UUID Yes Community
identifier query string Yes Meter code
effective_at query date No Day to evaluate the version

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/by-identifier?identifier=PT0002000000000000000000001234" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "identifier": "PT0002000000000000000000001234",
  "type": "CPE",
  "status": "ACTIVE",
  "start_date": "2026-01-01",
  "end_date": null
}

Tip

Returns 404 "Meter not found" when no such meter exists. Without effective_at the lookup ignores validity and only excludes meters whose stable status is DELETED; with effective_at it requires a version valid on that day.

Get a Meter by ID

GET /core/api/communities/{community_id}/meters/{meter_id}

Returns the meter version valid on effective_at.

Auth / permissions: any community member or admin (member-scoped: 403 "Access denied" for a meter the member does not reach through an owned resource).

Parameter In Type Required Description
community_id path UUID Yes Community
meter_id path UUID Yes Meter
effective_at query date No Day to evaluate. Default: today

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "identifier": "PT0002000000000000000000001234",
  "type": "CPE",
  "installation_type": "IC",
  "status": "ACTIVE",
  "version_id": "9b2f1c1e-6d3a-4a55-8f7e-1d2c3b4a5f60",
  "start_date": "2026-01-01",
  "end_date": null
}

Tip

404 "Meter not found" when the meter has no non-deleted version valid on that day.

Update a Meter

PUT /core/api/communities/{community_id}/meters/{meter_id}

Changes a meter from start_date (or for [start_date, end_date]). Fields that are not sent keep the values of the version valid on start_date; earlier history is untouched. Any version already covering the period is replaced (older and newer parts of it are kept as fragments).

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
meter_id path UUID Yes Meter
start_date body date Yes First day the change applies
end_date body date No Last day the change applies. null = open-ended
installation_type body enum No IC, IPR, IA, IAC
production_identifier body string No Injection-point code
ord body string No DSO name
cpe_id body UUID No New parent CPE (internal meters only)
status body enum No ACTIVE, INACTIVE, DELETED
identifier / type body - No Accepted only if equal to the current value; changing them returns 400

Example

curl -X PUT https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "INACTIVE",
    "start_date": "2026-03-01"
  }'

Response (abridged)

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "identifier": "PT0002000000000000000000001234",
  "type": "CPE",
  "status": "INACTIVE",
  "installation_type": "IC"
}

Tip

  • identifier and type are immutable (400). To replace a physical meter, end the old one and create a new one.
  • 403 "Meter version with effective_date: ... does not exist." when no version of the meter is valid on start_date.
  • 422 when cpe_id is set on a non-internal meter, or the parent is not a CPE of the same community.
  • The response is the stable meter record: it echoes the updated fields but does not carry start_date / end_date. Use GET ...?effective_at= to read a specific version.

End a Meter

DELETE /core/api/communities/{community_id}/meters/{meter_id}

Ends the meter from effective_at (versions from that day on are removed, earlier history is kept) and sets its stable status to INACTIVE.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
meter_id path UUID Yes Meter
effective_at query date Yes First day the meter no longer exists

Example

curl -X DELETE "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response

204 No Content on success. When a resource is still assigned to the meter on or after effective_at, nothing is deleted and the call answers 200 with:

{
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "message": "Contador está associado a um recurso e não pode ser removido. Por favor remova a associação."
}

Tip

The "assigned to a resource" refusal is a 200 response with a message, not an error status, so check for 204 (or for message) in your client. First end or reassign the resource (see Update a Resource).

Contracts

Base path: /core/api/communities/{community_id}/meters/{meter_id}/contracts

A contract defines the commercial terms of a CPE or aggregator meter for a period. See Contracts for the fields and the buy/sell payloads.

Key field Type Description
buy_type, sell_type enum NO_VALUES, FIXED, INDEXED, INDEXED_SIMPLIFIED
voltage_level enum For example MAT, AT, MT, BTE, BT, BTN simples, BTN 6.9 kVA (full list in the schema)
tariff_cycle enum TETRA_SEMANAL, TETRA_OPCIONAL, DIARIO, TRI_DIARIO, BI_DIARIO, TRI_SEMANAL, BI_SEMANAL
with_holidays bool Holidays treated as Sundays
contracted_power float kVA
start_date / end_date date Inclusive period; end_date optional and must not be before start_date
user_id, meter_id UUID Member responsible for the contract and the meter it is attached to
buy_payload, sell_payload object Tariff values, shape chosen by buy_type / sell_type (below)

Payload shapes:

Contract type Payload fields
FIXED buy_or_sell_type, peak_value, mid_peak_value, off_peak_value, super_off_peak_value, multiplierPMD (default 0), multiplierERSE (default 0)
INDEXED buy_or_sell_type and the 16 coefficients k1_, k2_, k5_, k6_ x peak_value, mid_peak_value, off_peak_value, super_off_peak_value
INDEXED_SIMPLIFIED buy_or_sell_type, multiplierPMD, multiplierERSE, additiveC

buy_or_sell_type is CONTRACT_BUY_VALUES for buy_payload and CONTRACT_SELL_VALUES for sell_payload. With NO_VALUES no payload is stored; opportunity costs must be uploaded instead.

Deprecated validity

validity is still accepted as an alias of start_date in contract request bodies. Responses always use start_date and end_date. Use start_date in new integrations.

Create a Contract

POST /core/api/communities/{community_id}/meters/{meter_id}/contracts

Creates a contract for the meter in meter_id of the body, for [start_date, end_date].

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id, meter_id path UUID Yes Community and meter (the meter must exist)
buy_type, sell_type body enum Yes See table above
voltage_level body enum Yes Voltage level
tariff_cycle body enum Yes Tariff cycle
with_holidays body bool Yes Holidays as Sundays
contracted_power body float Yes kVA
start_date body date Yes First day (inclusive)
end_date body date No Last day (inclusive)
user_id body UUID Yes Responsible member
meter_id body UUID Yes CPE or aggregator meter. Should equal the path meter_id
buy_payload, sell_payload body object No Tariff values

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7/contracts \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "buy_type": "FIXED",
    "sell_type": "FIXED",
    "voltage_level": "BTN simples",
    "tariff_cycle": "TETRA_SEMANAL",
    "with_holidays": true,
    "contracted_power": 6.9,
    "start_date": "2026-01-01",
    "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "buy_payload": {
      "buy_or_sell_type": "CONTRACT_BUY_VALUES",
      "peak_value": 0.21,
      "mid_peak_value": 0.17,
      "off_peak_value": 0.12,
      "super_off_peak_value": 0.10,
      "multiplierPMD": 1,
      "multiplierERSE": 1
    },
    "sell_payload": {
      "buy_or_sell_type": "CONTRACT_SELL_VALUES",
      "peak_value": 0.06,
      "mid_peak_value": 0.05,
      "off_peak_value": 0.04,
      "super_off_peak_value": 0.03,
      "multiplierPMD": 1,
      "multiplierERSE": 0
    }
  }'

Response (abridged, 201 Created)

{
  "id": "a1b2c3d4-0000-4000-8000-000000000001",
  "version_id": "a1b2c3d4-0000-4000-8000-000000000001",
  "buy_type": "FIXED",
  "sell_type": "FIXED",
  "voltage_level": "BTN simples",
  "tariff_cycle": "TETRA_SEMANAL",
  "with_holidays": true,
  "contracted_power": 6.9,
  "start_date": "2026-01-01",
  "end_date": null,
  "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}

Tip

  • Returns 409 when the meter already has a contract starting on exactly the same start_date.
  • Any other contract that overlaps the new period is trimmed or split (its parts outside [start_date, end_date] are kept as new contract versions) instead of being rejected, so the new contract takes precedence over the period.
  • The meter (from the body meter_id, valid on start_date) must be a CPE or AGGREGATOR_METER, otherwise 400 "Meter is not CPE". An unknown meter returns 403.
  • end_date before start_date returns 422.
  • The tariff payload is stored according to the type: FIXED as a fixed tariff, INDEXED as coefficients, INDEXED_SIMPLIFIED as the simplified index. NO_VALUES stores none.
  • Every contract version has its own id; the response version_id equals id.
  • The path meter_id is only checked for existence; the body meter_id decides which meter receives the contract, so send the same value in both.

Clone a Contract

POST /core/api/communities/{community_id}/meters/{meter_id}/contracts/{contract_id}/clone

Creates a new contract from the stored values of contract_id (general fields plus buy and sell payloads). The request has no body.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id, meter_id path UUID Yes Community and meter
contract_id path UUID Yes Contract to copy

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7/contracts/a1b2c3d4-0000-4000-8000-000000000001/clone \
  -H "Authorization: Bearer $TOKEN"

Response (abridged, 201 Created)

{
  "id": "a1b2c3d4-0000-4000-8000-000000000002",
  "version_id": "a1b2c3d4-0000-4000-8000-000000000002",
  "buy_type": "FIXED",
  "sell_type": "FIXED",
  "start_date": "2026-01-01",
  "end_date": null,
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}

Tip

The copy keeps the original's start_date and end_date, and the clone goes through the same overlap handling as Create a Contract. Because the periods are identical, the copy replaces the original: the response carries a new id and the original contract is no longer returned. To obtain a copy with different dates or values, read the contract and POST a new one (or PUT the contract).

List Contracts of a Meter

GET /core/api/communities/{community_id}/meters/{meter_id}/contracts

Returns the contract(s) of the meter whose period contains effective_at, with their buy and sell payloads.

Auth / permissions: any community member or admin (member-scoped: 403 "Access denied" when a member does not reach the meter through an owned resource).

Parameter In Type Required Description
community_id, meter_id path UUID Yes Community and meter
effective_at query date No Day to evaluate. Default: today

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7/contracts?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "a1b2c3d4-0000-4000-8000-000000000001",
    "buy_type": "FIXED",
    "sell_type": "FIXED",
    "voltage_level": "BTN simples",
    "tariff_cycle": "TETRA_SEMANAL",
    "with_holidays": true,
    "contracted_power": 6.9,
    "start_date": "2026-01-01",
    "end_date": null,
    "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "buy_payload": {
      "buy_or_sell_type": "CONTRACT_BUY_VALUES",
      "contract_id": "a1b2c3d4-0000-4000-8000-000000000001",
      "peak_value": 0.21,
      "mid_peak_value": 0.17,
      "off_peak_value": 0.12,
      "super_off_peak_value": 0.10,
      "multiplierPMD": 1.0,
      "multiplierERSE": 1.0
    },
    "sell_payload": { "buy_or_sell_type": "CONTRACT_SELL_VALUES", "peak_value": 0.06 }
  }
]

Tip

When no contract covers effective_at the call fails with 403 "Contracts not found for meter." (the platform's not-found status), rather than returning an empty list. A gap between two contracts therefore shows up as this error.

Update a Contract

PUT /core/api/communities/{community_id}/meters/{meter_id}/contracts/{contract_id}

Replaces the meter's contract for [start_date, end_date] with the full body. The body has the same fields as Create a Contract; it is a full replacement of the values (general fields and payloads), not a partial patch.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id, meter_id path UUID Yes Community and meter
contract_id path UUID Yes An existing contract of the meter
body body object Yes Same schema as the create body. meter_id must equal the contract's meter

Example

curl -X PUT https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7/contracts/a1b2c3d4-0000-4000-8000-000000000001 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "buy_type": "NO_VALUES",
    "sell_type": "NO_VALUES",
    "voltage_level": "BTN simples",
    "tariff_cycle": "TETRA_SEMANAL",
    "with_holidays": true,
    "contracted_power": 10.35,
    "start_date": "2026-07-01",
    "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
  }'

Response (abridged, 200 OK)

{
  "id": "a1b2c3d4-0000-4000-8000-000000000003",
  "version_id": "a1b2c3d4-0000-4000-8000-000000000003",
  "buy_type": "NO_VALUES",
  "sell_type": "NO_VALUES",
  "contracted_power": 10.35,
  "start_date": "2026-07-01",
  "end_date": null,
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}

Tip

  • The update is a replacement over the period: every contract of the meter overlapping [start_date, end_date] is removed (their parts before start_date and after end_date are kept as new contract versions) and one new contract is created. The response therefore has a new id.
  • In the example above, the January contract is kept for 2026-01-01 to 2026-06-30 and the new terms apply from July 1.
  • The meter_id of the body cannot differ from the contract's meter. This is raised as an unhandled error and answered with 500; create a new contract on the other meter instead.

Delete a Contract

DELETE /core/api/communities/{community_id}/meters/{meter_id}/contracts/{contract_id}

Removes the contract from effective_at onwards, or only for [effective_at, end_date] when end_date is given. The parts of the contract outside the removed period are kept.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id, meter_id path UUID Yes Community and meter
contract_id path UUID Yes Contract
effective_at query date Yes First day removed (raised to the contract's start_date if earlier)
end_date query date No Last day removed. null = remove to the end of the contract

Example

curl -X DELETE "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/meters/7c9e6679-7425-40de-944b-e07fc1f90ae7/contracts/a1b2c3d4-0000-4000-8000-000000000001?effective_at=2026-06-01&end_date=2026-06-30" \
  -H "Authorization: Bearer $TOKEN"

Response

204 No Content.

Tip

  • Removing a period in the middle of a contract splits it in two new contract versions (with new IDs), leaving a gap for the removed days.
  • end_date before effective_at is answered with 500 (unhandled error).
  • If effective_at is after the contract's end_date, nothing changes but the call still returns 204.
  • Market and settlement need a contract on every CPE for every processed day, so avoid gaps.

Resources

Base path: /core/api/communities/{community_id}/resources

Resource types are IU, IC, SCPU, STORAGE, EV, EWH and HP. See Resources for the meaning of each type and field. The general fields (name, status, meter_id) and the type-specific details are versioned together.

Create a Resource

POST /core/api/communities/{community_id}/resources

Creates a resource together with its type-specific details and, optionally, its ownership shares, as one dated record.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
resource_payload.name body string Yes Name, unique in the community (case-insensitive, whitespace trimmed, across all history)
resource_payload.type body enum Yes IU, IC, SCPU, STORAGE, EV, EWH, HP. Fixed after creation
resource_payload.status body enum Yes ACTIVE, INACTIVE, DELETED
resource_payload.community_id body UUID Yes Must match the path community
resource_payload.meter_id body UUID No Meter directly above the resource. Without it the resource is view-only
resource_payload.start_date body date Yes First day (inclusive)
resource_payload.end_date body date No Last day (inclusive)
iu_payload, ic_payload body object One of sum_certified_power (kW, required), ic_voltage_level (required), description
scpu_payload body object One of generation_capacity (kW, required)
storage_payload body object One of nominal_capacity, max_power, charging_efficiency, discharging_efficiency, min_SOC, max_SOC (all required), deg_cost, init_soc
ev_payload body object One of max_power_input, max_power_output, charging_efficiency, discharging_efficiency, min_SOC, max_SOC (required), nominal_capacity, desired_min_SOC, deg_cost, init_soc
ewh_payload body object One of capacity, power, max_temp, default_start_temp, target_temp (all required)
hp_payload body object One of Heat-pump parameters, all optional (specification in development)
ownerships_payload body array No Items {user_id, perc_ownership, start_date, end_date?}

At least one *_payload is required, and the one matching resource_payload.type must be present.

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "resource_payload": {
      "name": "Battery 1",
      "type": "STORAGE",
      "status": "ACTIVE",
      "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "start_date": "2026-01-01"
    },
    "ownerships_payload": [
      {
        "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
        "perc_ownership": 100,
        "start_date": "2026-01-01"
      }
    ],
    "storage_payload": {
      "nominal_capacity": 10.0,
      "max_power": 5.0,
      "charging_efficiency": 95,
      "discharging_efficiency": 95,
      "min_SOC": 10,
      "max_SOC": 100
    }
  }'

Response (abridged, 201 Created)

{
  "id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "name": "Battery 1",
  "type": "STORAGE",
  "status": "ACTIVE",
  "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "details": {
    "nominal_capacity": 10.0,
    "max_power": 5.0,
    "charging_efficiency": 95.0,
    "discharging_efficiency": 95.0,
    "min_SOC": 10.0,
    "max_SOC": 100.0
  },
  "total_ownership": 100.0
}

Tip

  • Returns 422 when no type payload is sent, the name is empty, the ownership list repeats a user or sums to more than 100, or the meter is not valid for the whole resource period ("The resource period must be covered by the meter's validity.").
  • Returns 409 when the name is already used in the community, or when the meter_id (a CPE or INTERNAL meter) already has another resource on any day of the period. Put each extra resource behind its own internal meter. An unknown meter, or one not valid on start_date, returns 403.
  • Everything (resource, version, details, ownerships) is saved in one transaction.
  • To see the dated ownership shares, read the resource with GET after creating it.
  • charging_efficiency and discharging_efficiency are percentages; *_SOC are percentages; capacities are kWh and powers kW.

List Meters Available for a Resource

GET /core/api/communities/{community_id}/resources/available-meters

Lists the meters that can receive a resource for the whole period: meters valid on start_date that have no other resource in [start_date, end_date]. Use it to fill the meter picker before creating or updating a resource.

Auth / permissions: any community member or admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day of the period
end_date query date No Last day of the period. Omit for open-ended
resource_id query UUID No Ignore this resource's own assignment (use when editing it)

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources/available-meters?start_date=2026-01-01&end_date=2026-12-31" \
  -H "Authorization: Bearer $TOKEN"

Response

[
  {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "identifier": "PT0002000000000000000000001234",
    "type": "CPE"
  }
]

Tip

Only CPE and INTERNAL meters are excluded when occupied; an AGGREGATOR_METER is always listed. Deleted meters and meters with no version valid on start_date are not listed. The list is ordered by identifier.

List Resources

GET /core/api/communities/{community_id}/resources

Lists the resources valid on effective_at, with type details, ownerships and total_ownership.

Auth / permissions: any community member or admin (member-scoped: members only get the resources they own).

Parameter In Type Required Description
community_id path UUID Yes Community
effective_at query date No Day to evaluate. Default: today
exclude_deleted query string No true (default) hides resources with status DELETED. Send false to include them

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
    "name": "Battery 1",
    "type": "STORAGE",
    "status": "ACTIVE",
    "created_at": "2026-01-01T09:00:00Z",
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "version_id": "5e4d3c2b-1a09-4f8e-8d7c-6b5a49382716",
    "start_date": "2026-01-01",
    "end_date": null,
    "details": { "nominal_capacity": 10.0, "max_power": 5.0 },
    "total_ownership": 100.0,
    "ownerships": [
      {
        "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
        "resource_id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
        "perc_ownership": 100.0,
        "start_date": "2026-01-01",
        "end_date": null
      }
    ]
  }
]

Tip

  • A resource with status: DELETED that cannot be fully removed (because sessions or settlements reference it) is hidden unless exclude_deleted=false.
  • Returns [] when there are no resources for the day.
  • total_ownership is the sum of the shares valid on effective_at; it is below 100 when the resource is only partly allocated.

Get a Resource by Name

GET /core/api/communities/{community_id}/resources/by-name

Finds a resource by name inside the community (case-insensitive).

Auth / permissions: any community member or admin (member-scoped: 403 "Access denied" for a resource the member does not own).

Parameter In Type Required Description
community_id path UUID Yes Community
name query string Yes Resource name
effective_at query date No Day to evaluate. Default: today

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources/by-name?name=battery%201" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "name": "Battery 1",
  "type": "STORAGE",
  "status": "ACTIVE",
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "start_date": "2026-01-01",
  "end_date": null,
  "details": { "nominal_capacity": 10.0, "max_power": 5.0 },
  "ownerships": []
}

Tip

404 "Resource not found" when no resource has that name on the day. The match uses SQL ILIKE, so % and _ in the name act as wildcards.

List Resources by Meter

GET /core/api/communities/{community_id}/resources/meter/{meter_id}

Returns the resources assigned to a meter on effective_at, with details and ownerships.

Auth / permissions: any community member or admin (not member-scoped).

Parameter In Type Required Description
community_id, meter_id path UUID Yes Community and meter
effective_at query date No Day to evaluate. Default: today
exclude_deleted query string No true (default) hides DELETED resources. Send false to include them

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources/meter/7c9e6679-7425-40de-944b-e07fc1f90ae7?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
    "name": "Battery 1",
    "type": "STORAGE",
    "status": "ACTIVE",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "details": { "nominal_capacity": 10.0, "max_power": 5.0 },
    "ownerships": []
  }
]

Tip

When the meter has no resource on that day the call fails with 403 "Resources for meter with meter_id: ... does not exist." (the platform's not-found status), not with an empty list. The path community_id is not matched against the meter's community.

Get a Resource by ID

GET /core/api/communities/{community_id}/resources/{resource_id}

Returns the resource version valid on effective_at, with type details, ownerships and total_ownership.

Auth / permissions: any community member or admin (member-scoped: 403 "Access denied" for a resource the member does not own).

Parameter In Type Required Description
community_id, resource_id path UUID Yes Community and resource
effective_at query date No Day to evaluate. Default: today

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources/0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10?effective_at=2026-01-14" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "name": "Battery 1",
  "type": "STORAGE",
  "status": "ACTIVE",
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "version_id": "5e4d3c2b-1a09-4f8e-8d7c-6b5a49382716",
  "start_date": "2026-01-14",
  "end_date": null,
  "details": { "nominal_capacity": 10.0, "max_power": 10.0 },
  "total_ownership": 100.0,
  "ownerships": [
    { "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11", "perc_ownership": 100.0 }
  ]
}

Tip

details depends on the resource type (see the field tables in Resources). A resource with no version on effective_at returns 403 "Resource with id: ... does not exist.".

Update a Resource

PUT /core/api/communities/{community_id}/resources/{resource_id}

Creates or replaces a dated revision of the resource from start_date (or for [start_date, end_date]). General fields, type details, meter assignment and ownerships are saved together.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id, resource_id path UUID Yes Community and resource
start_date body date Yes First day the change applies
end_date body date No Last day the change applies
name body string No New name. Default: current name
status body enum No New status. Default: current status
type body enum No Accepted only if equal to the current type
details body object No Type-specific fields to change. Merged over the current values
meter_id body UUID or null No New meter. null = view-only for the period; omitted = keep current meter
ownerships_payload body array No Full list of shares for the period (user_id, perc_ownership, start_date, end_date)

Example

Upgrading the battery to 10 kW from January 14:

curl -X PUT https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources/0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "details": { "max_power": 10.0 },
    "start_date": "2026-01-14"
  }'

Response (abridged)

{
  "id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "name": "Battery 1",
  "type": "STORAGE",
  "status": "ACTIVE",
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "version_id": "5e4d3c2b-1a09-4f8e-8d7c-6b5a49382716",
  "start_date": "2026-01-14",
  "end_date": null,
  "details": { "nominal_capacity": 10.0, "max_power": 10.0 }
}

Tip

  • Days before January 14 keep the 5 kW version. Fields not sent are inherited from the version valid on start_date, and details only overrides the fields it contains.
  • When ownerships_payload is sent, members that currently have a share but are missing from the list lose it for the period; when omitted, ownerships are untouched.
  • 422 for a changed type, an empty name, a total share above 100, an end_date before start_date, or a meter that does not cover the period. 409 for a name already used in the community, or when the meter already has another resource in the period.
  • Unknown resource: 403.

End a Resource

DELETE /core/api/communities/{community_id}/resources/{resource_id}

Closes the resource's operational history from effective_at; earlier days remain available.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id, resource_id path UUID Yes Community and resource
effective_at query date Yes First day the resource no longer exists

Example

curl -X DELETE "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resources/0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response

204 No Content on success. When ownerships still exist on or after effective_at, nothing is deleted and the call answers 200 with:

{
  "resource_id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "message": "Recurso está alocado a um ou mais membros. Por favor remova a alocação."
}

Tip

The refusal is a 200 with a message, not an error status. End the ownerships first with End an Ownership using the same effective_at, then delete the resource.

Ownerships

Base path: /core/api/communities/{community_id}/ownerships

An ownership gives a member a percentage (perc_ownership, greater than 0 and at most 100) of a resource for a period. Compensation from transactions is split between owners in proportion to their shares (Resources). The total of all shares of a resource must not exceed 100% on any day.

Assign an Ownership

POST /core/api/communities/{community_id}/ownerships/{user_id}/{resource_id}

Gives the member a share of the resource for [start_date, end_date]. If the member already has an ownership record on the resource, the call behaves like an update of that share for the period.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id path UUID Yes Community
user_id path UUID Yes Member who owns the share
resource_id path UUID Yes Resource
perc_ownership body float Yes Share in percent, greater than 0 and at most 100
start_date body date Yes First day (inclusive)
end_date body date No Last day (inclusive)

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/ownerships/5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11/0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "perc_ownership": 70,
    "start_date": "2026-01-01"
  }'

Response (abridged, 201 Created)

{
  "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
  "resource_id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "perc_ownership": 70.0
}

Tip

  • 422 when perc_ownership is not greater than 0, 409 when it is above 100. If the other owners plus the new share exceed 100% on any day of the period, the call fails with an unhandled error (500, "Resource ownership exceeds 100 percent on YYYY-MM-DD").
  • An unknown user or resource returns 403.
  • The response echoes the stable ownership record, not the dated version. Use GET to read the versions.

List a Member's Ownerships

GET /core/api/communities/{community_id}/ownerships/{user_id}

Lists the resource shares the member holds in this community on effective_at.

Auth / permissions: any community member or admin (not member-scoped: the user_id in the path is not compared to the caller).

Parameter In Type Required Description
community_id path UUID Yes Community
user_id path UUID Yes Member
effective_at query date No Day to evaluate. Default: today

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/ownerships/5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response

[
  {
    "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
    "resource_id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
    "perc_ownership": 70.0,
    "version_id": "2a3b4c5d-6e7f-4081-92a3-b4c5d6e7f809",
    "start_date": "2026-01-01",
    "end_date": null
  }
]

Tip

Shares on resources of other communities are not returned. An empty list means the member has no valid share on that day.

Update an Ownership

PUT /core/api/communities/{community_id}/ownerships/{user_id}/{resource_id}

Sets the member's share to perc_ownership for [start_date, end_date], keeping the rest of the share's history.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id path UUID Yes Community
user_id, resource_id path UUID Yes Member and resource
perc_ownership body float Yes New share in percent (greater than 0, at most 100)
start_date body date Yes First day the new share applies
end_date body date No Last day the new share applies

Example

curl -X PUT https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/ownerships/5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11/0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "perc_ownership": 50,
    "start_date": "2026-07-01"
  }'

Response (abridged)

{
  "user_id": "5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11",
  "resource_id": "0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10",
  "perc_ownership": 50.0,
  "version_id": null,
  "start_date": null,
  "end_date": null
}

Tip

  • The 100% rule is checked on every day of the period against the other owners (500 with "Resource ownership exceeds 100 percent on YYYY-MM-DD" when it would be exceeded). A share of 0 or less is 422, above 100 is 409.
  • The response shows the stable record, so its dated fields are empty; read the dated shares with the list endpoint.
  • An unknown user or resource returns 403.

End an Ownership

DELETE /core/api/communities/{community_id}/ownerships/{user_id}/{resource_id}

Ends the member's share of the resource from effective_at (open-ended removal). Earlier days keep the share.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id path UUID Yes Community
user_id, resource_id path UUID Yes Member and resource
effective_at query date Yes First day the share no longer applies

Example

curl -X DELETE "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/ownerships/5d1f1a1c-2b6b-4a3e-9c0a-0e5f5c1a7b11/0c7e3a52-1f6b-4d2a-9a3b-3b1f7f9d2c10?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response

204 No Content.

Tip

Always returns 204 once the user and resource exist, even if the member had no share on that day. Remove all shares before ending the resource (see End a Resource).

RESP

Base path: /core/api/communities/{community_id}/resp

RESP (public grid) pairs say whether two CPEs are connected through the public grid only. Pairs are created automatically, in both directions, when a CPE is created (see Create a Meter). The is_active flag is dated. See RESP configuration.

List RESP Pairs

GET /core/api/communities/{community_id}/resp

Lists the RESP pairs valid on effective_at. Each pair of meters is returned once (the reverse direction is merged into it).

Auth / permissions: any community member or admin (not member-scoped).

Parameter In Type Required Description
community_id path UUID Yes Community
effective_at query date No Day to evaluate. Default: today

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resp?effective_at=2026-06-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "d4e5f6a7-b8c9-4d0e-8f1a-2b3c4d5e6f70",
    "provider_meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "receiver_meter_id": "b3a1c2d4-5e6f-4789-a0b1-c2d3e4f5a6b7",
    "is_active": true,
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "version_id": "e5f6a7b8-c9d0-4e1f-9a2b-3c4d5e6f7a81",
    "start_date": "2026-01-01",
    "end_date": null
  }
]

Tip

The list is ordered with active pairs first. Which direction's id is returned for a pair is not guaranteed, but PUT updates both directions, so either id can be used.

Update a RESP Pair

PUT /core/api/communities/{community_id}/resp/{resp_id}

Sets is_active for a RESP pair from effective_at (or for [effective_at, end_date]), and applies the same change to the opposite direction of the pair.

Auth / permissions: community admin or platform super-user.

Parameter In Type Required Description
community_id path UUID Yes Community
resp_id path UUID Yes RESP pair (as returned by the list)
effective_at query date Yes First day of the change
end_date query date No Last day of the change. Omit for open-ended
body body object No A full RESP object (provider_meter_id, receiver_meter_id, is_active, community_id, start_date); only its is_active is used. Omit the body to toggle

Example

curl -X PUT "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/resp/d4e5f6a7-b8c9-4d0e-8f1a-2b3c4d5e6f70?effective_at=2026-07-01&end_date=2026-09-30" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "provider_meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "receiver_meter_id": "b3a1c2d4-5e6f-4789-a0b1-c2d3e4f5a6b7",
    "is_active": false,
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "start_date": "2026-07-01"
  }'

Response

{ "detail": "RESP pair updated successfully" }

Tip

  • Without a body the call toggles: the pair gets the opposite of its is_active on effective_at.
  • If a body is sent it must be a complete RESP object (else 422); the dates inside it are ignored and effective_at / end_date from the query are used.
  • Returns 403 "RESP version with effective_date: ... does not exist." when the pair has no version valid on effective_at.
  • Market sessions of the affected period are invalidated so they are recomputed.