Use Case "Creation and prequalification of a PQ"
Paul Garus
Johanna Gemassmer
This page shows steps on how to use the PQ-API to create, display, modify and prequalify PQs.
The first step is to create a PQ for a reserve unit or reserve group. To do this, the IDs of the reserve unit/reserve group must be determined. The following request has to be done for a reserve unit:
curl -X 'GET' \
'https://training.pq-portal.energy/pqapi/data/v1/reserve-resources?types=ReserveUnit' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>'
It is similar for a reserve group:
curl -X 'GET' \
'https://training.pq-portal.energy/pqapi/data/v1/reserve-resources?types=ReserveGroup' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>'
As a result of the first step, the ID of the reserve unit/reserve group for which we want to create a PQ is displayed as response and should be noted for the next steps.
Response body:
{
"id": "........-....-....-....-............"
}
A PQ can now be created using the ID of the unit. The following request is used to create a PQ for the reserve unit:
curl -X 'POST' \
'https://training.pq-portal.energy/pqapi/data/v1/reserve-units/{id}/prequalifications' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json' \
-d '{
"reserveType": "FrequencyContainmentReserve", <--- mandatory
"reserveDirection": "Negative", <--- mandatory
"requestedMeasurement": { <--- not mandatory
"reactionTime": ...,
"deliveryDuration": ...,
"fullLoadHours": ...,
"power": ...,
"marketablePower": ...,
"powerSlewRate": ...
}
}'
The information on the reserve type and the reserve direction is mandatory. The requested measurements do not necessarily have to be specified. Important: There must be no existing PQ of the same type and direction for this unit.
Note: An overview of mandatory fields can be found at the end of the Swagger documentation. The schemas below show the last endpoint definition and apply across all endpoints. The mandatory information is marked with a red asterisk:
After the request has been made the response body contains the GUID of the new PQ:
{
"id": "........-....-....-....-............"
}
If a typing error occurs when entering one of the mandatory fields, the error message will inform you of this. In the following example, the “e” at the end of the “Negative” reserve direction is forgotten.
[...]
{
"domain": "Pq_Api",
"category": "RequestValidation",
"errorCode": "DataTransferObjectNotValid",
"message": "The reserveDirection must be of type Lotes.PQ.Api.Applications.Models.Enumerations.ReserveDirection",
"contextType": "Field",
"context": {
"fieldName": "reserveDirection",
"typeName": "FieldValidationContext"
}
}
[...]
Creating a PQ for a reserve group can be carried out in the same way as a reserve unit:
curl -X 'POST' \
'https://training.pq-portal.energy/pqapi/data/v1/reserve-groups/{id}/prequalifications' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json' \
-d '{
"reserveType": "FrequencyContainmentReserve",
"reserveDirection": "Negative",
"requestedMeasurement": { <--- not mandatory
"reactionTime": ...,
"deliveryDuration": ...,
"fullLoadHours": ...,
"power": ...,
"marketablePower": ...,
"powerSlewRate": ...
}
}'
The response body of the reserve group is identical to the reserve unit.
For example, if the GUID is not known to the system, the following error is displayed.
{
"statusCode": 404,
"domain": "Pq_Api",
"category": "Business",
"errorCode": "ResourceNotFound",
"message": "Resource not found.",
"reasons": [
{
"domain": "Pq_Api",
"category": "Business",
"errorCode": "ReserveResourceDoesNotExist",
"message": "The reserve resource 93897c75-7417-4c57-99d6-ba0fb6c2f545 does not exist.",
"contextType": "Reference",
"context": {
"id": "93897c75-7417-4c57-99d6-ba0fb6c2f545",
"typeName": "ReferenceContext"
}
}
]
}
The 'message' parameter specifies what the problem is. In this case, the GUID does not exist. It must be an input error.
The PQ we have just created can now be displayed. For a reserve unit, the API request might look like this:
curl -X 'GET' \
'https://training.pq-portal.energy/pqapi/data/v1/reserve-units/{id}/prequalifications' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>'
For an reserve group, the API call might look like this:
curl -X 'GET' \
'https://training.pq-portal.energy/pqapi/data/v1/reserve-groups/{id}/prequalifications' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>'
The response body might look like this:
[
{
"id": "........-....-....-....-............",
"number": "...............",
"transmissionOperatorId": "........-....-....-....-............",
"balancingServiceProviderId": "........-....-....-....-............",
"type": "FirstPrequalification",
"state": "Prequalified",
"reserveResourceId": "........-....-....-....-............",
"reserveResourceType": "ReserveUnit",
"reserveType": "FrequencyContainmentReserve",
"reserveDirection": "Positive"
},
{
There are two methods for updating PQ-API data: PUT and PATCH. The Swagger documentation specifies in the request body which parameters can be changed using the PUT method. By default, a PUT sets all parameters defined by the request body to null if they are not explicitly specified. It is therefore advisable to specify all possible parameters in a PUT request. The PQ-API offers two PUT methods for PQs. The first PUT method allows you to customize the requested measurement data. This endpoint is available for Balancing Service Providers. To process this data, all rules of the PQ-Portal apply, such as the PQ must be in status 'planned'.
curl -X 'PUT' \
'https://training.pq-portal.energy/pqapi/data/v1/prequalifications/{id}/requested-measurement' \
-H 'accept: */*' \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json' \
-d '{
"reactionTime": 100,
"deliveryDuration": 100,
"fullLoadHours": 100,
"power": 100,
"marketablePower": 100,
"powerSlewRate": 100
}'
The PQ is successfully updated when HTTP code 204 is returned. In this example, all possible parameters have been set with values. If a parameter had been removed from the request body (for example reactionTime), this parameter would have been set to null.
curl -X 'PUT' \
'https://training.pq-portal.energy/pqapi/data/v1/prequalifications/{id}/requested-measurement' \
-H 'accept: */*' \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json' \
-d '{
"deliveryDuration": 1000,
"fullLoadHours": 1000,
"power": 1000,
"marketablePower": 1000,
"powerSlewRate": 1000
}'
Now the PQ data look like this:
{
...
"requestedMeasurement": {
"reactionTime": null, <--- reactionTime has no value anymore
"deliveryDuration": 1000,
"fullLoadHours": 1000,
"power": 1000,
"marketablePower": 1000,
"powerSlewRate": 1000
},
...
You can also use the PATCH method to modify data. For example you can use PATCH and the prequalification endpoint to change the date of expiration for a prequalification under validUntil
. In principle, a PATCH works differently compared to a PUT and requires input in the request body in JSON patch rfc6902 format based on schema PatchPrequalification.
“op” = operation to perform
”path” = path to the parameter you want to modify
“from” = just needed for the operations ‘copy’ and ‘move’ (irrelevant for this example)
”value” = the value of the parameter
Several parameters can also be modified at the same time.
The example value schema of a request body looks like this:
[
{
"op": "add",
"path": "string",
"from": "string",
"value": "string"
}
]
Updating the parameter validUntil
is only allowed for PQs in status prequalified.
Important information: the Content-Type must be defined as application/json-patch+json.
To set the parameter validUntil
to the new date 2028-01-16 the full request looks like this:
curl -X 'PATCH' \
'https://training.pq-portal.energy/pqapi/data/v1/prequalifications/{id}' \
-H 'accept: */*' \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json-patch+json' \
-d '[
{
"op": "add",
"path": "/validUntil",
"value": "2028-01-16T00:00:00Z"
}
]'
Apart from the parameter validUntil
, nothing has been changed in the PQ.
The new date for validUntil
is now visible in the PQ by using the following request:
curl -X 'GET' \
'https://training.pq-portal.energy/pqapi/data/v1/prequalifications/{id}' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <accessToken>'
Response body:
{
...
"validFrom": "2024-01-16T00:00:00Z",
"validUntil": "2028-01-16T00:00:00Z", <--- new date
...
}
The final step is to request the PQ. If not all the requirements for prequalification are met, the response of the endpoint will indicate this within the error message. The PQ ID is required to execute the endpoint. The requested action is written into the request body:
curl -X 'POST' \
'https://training.pq-portal.energy/pqapi/data/v1/prequalifications/{id}/action' \
-H 'accept: */*' \
-H 'Authorization: Bearer <accessToken>' \
-H 'Content-Type: application/json' \
-d '{
"action": "Request"
}'
The PQ is successfully requested when HTTP code 204 is returned.
For example, if an incorrect action is carried out (prequalify instead of request), the error message 'forbidden' indicates the reason as you can see below:
{
"statusCode": 403,
"domain": "Pq_Api",
"category": "Permission",
"errorCode": "InsufficientPermissions",
"message": "Insufficient permissions.",
"reasons": [
{
"domain": "Pq_Api",
"category": "Permission",
"errorCode": "PrincipalDoesNotHavePermission",
"message": "The principal (User) {id} does not have the permission ActivatePrequalification.",
"contextType": "Permission",
"context": {
"userId": "...",
"permissionTypes": [
"ActivatePrequalification"
],
"typeName": "PermissionContext"
}
}
]
}