{
  "openapi": "3.1.0",
  "info": {
    "title": "Battery Compliance DPP API (EN 18222)",
    "version": "1.0.0",
    "description": "Life Cycle API and Fine Granular API for digital battery passports, following the method names and status codes of EN 18222 as described in BatteryPass-Ready “User Stories from a Technical Perspective” v1.0 (tables 1, 2, 4). DPP ID = Product ID = the permanent item URI; in paths it is URL-encoded (encodeURIComponent). Reading public data needs no token. Controlled data, search and updates need an OAuth 2.0 access token (client credentials, POST /oauth/token). Responses: { statusCode, payload?, result? }. A passport payload is { dpp, version, accessLevel, links }: dpp is the signed passport filtered to the caller's access level; links lists the Art. 77(7) links to the passports of the original batteries (predecessors) and to new passports (successors) – they are not part of the signed passport."
  },
  "servers": [
    {
      "url": "https://batterycompliance.eu"
    }
  ],
  "paths": {
    "/oauth/token": {
      "post": {
        "summary": "Obtain an access token (OAuth 2.0 client credentials)",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type"
                ],
                "properties": {
                  "grant_type": {
                    "const": "client_credentials"
                  },
                  "scope": {
                    "type": "string",
                    "description": "space-separated subset of dpp:read dpp:search dpp:write"
                  },
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  }
                }
              },
              "example": "grant_type=client_credentials&scope=dpp%3Aread"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token",
            "content": {
              "application/json": {
                "example": {
                  "access_token": "eyJhbGciOiJFZERTQSIs…",
                  "token_type": "Bearer",
                  "expires_in": 900,
                  "scope": "dpp:read dpp:write"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request, unsupported_grant_type, invalid_scope"
          },
          "401": {
            "description": "invalid_client"
          }
        }
      }
    },
    "/api/dpp/v1/dpps": {
      "get": {
        "operationId": "ReadDPPByProductId",
        "summary": "ReadDPPByProductId – current active DPP for a Product ID",
        "security": [
          {},
          {
            "oauth2": [
              "dpp:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "productId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Active DPP filtered to the caller's access rights",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dpp": {
                      "dppHeader": {
                        "digitalProductPassportId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                        "dppStatus": {
                          "dppStatusValue": "active"
                        }
                      },
                      "performanceAndDurability": {
                        "stateOfCharge": {
                          "percentageValue": 80,
                          "percentUnit": "%"
                        }
                      }
                    },
                    "version": {
                      "versionNo": 3,
                      "publishedAt": "2026-10-01T08:00:00.000Z",
                      "sha256": "9f2c…",
                      "signature": "base64…"
                    },
                    "accessLevel": "legitimateInterest",
                    "links": {
                      "predecessors": [],
                      "successors": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          }
        }
      },
      "post": {
        "operationId": "CreateDPP",
        "summary": "CreateDPP – not provided (501 ServerNotImplemented)",
        "responses": {
          "501": {
            "description": "Not implemented",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/api/dpp/v1/dpps/{dppId}": {
      "get": {
        "operationId": "ReadDPPById",
        "summary": "ReadDPPById – latest version of a DPP, whatever its status",
        "security": [
          {},
          {
            "oauth2": [
              "dpp:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          }
        ],
        "responses": {
          "200": {
            "description": "DPP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dpp": {
                      "dppHeader": {
                        "digitalProductPassportId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                        "dppStatus": {
                          "dppStatusValue": "active"
                        }
                      },
                      "performanceAndDurability": {
                        "stateOfCharge": {
                          "percentageValue": 80,
                          "percentUnit": "%"
                        }
                      }
                    },
                    "version": {
                      "versionNo": 3,
                      "publishedAt": "2026-10-01T08:00:00.000Z",
                      "sha256": "9f2c…",
                      "signature": "base64…"
                    },
                    "accessLevel": "legitimateInterest",
                    "links": {
                      "predecessors": [],
                      "successors": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          }
        }
      },
      "patch": {
        "operationId": "UpdateDPPById",
        "summary": "UpdateDPPById – partial update of item-level data elements; creates a new signed version",
        "description": "Body: partial DPP by section. `null` removes an element. Every element must be within the client's write paths; identifiers, header and model data are immutable. Only active DPPs. All-or-nothing validation against the schema and the category profile. Optional Idempotency-Key header. Recycling (Art. 77(8)): `{\"dppHeader\":{\"dppStatus\":{\"dppStatusValue\":\"archived\"}},\"identifiersAndProductData\":{\"batteryStatus\":{\"batteryStatusValues\":\"waste\"}}}` with the scope dpp:recycle records that the battery was recycled – a last archived version is published and the passport is frozen (the \"Marked for Deletion\" step of BatteryPass-Ready, with the EN 18223 statuses). At most one automatic version a day per passport: a later change is stored and published when the day is over (the response gives publishAfter); the battery status, the putting into service and accidents are published at once.",
        "security": [
          {
            "oauth2": [
              "dpp:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "identifiersAndProductData": {
                  "batteryStatus": {
                    "batteryStatusValues": "repurposed"
                  }
                },
                "performanceAndDurability": {
                  "stateOfCharge": {
                    "percentageValue": 80
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated DPP (new version)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dpp": {
                      "dppHeader": {
                        "digitalProductPassportId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                        "dppStatus": {
                          "dppStatusValue": "active"
                        }
                      },
                      "performanceAndDurability": {
                        "stateOfCharge": {
                          "percentageValue": 80,
                          "percentUnit": "%"
                        }
                      }
                    },
                    "version": {
                      "versionNo": 3,
                      "publishedAt": "2026-10-01T08:00:00.000Z",
                      "sha256": "9f2c…",
                      "signature": "base64…"
                    },
                    "accessLevel": "legitimateInterest",
                    "links": {
                      "predecessors": [],
                      "successors": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          }
        }
      },
      "delete": {
        "operationId": "DeleteDPPById",
        "summary": "DeleteDPPById – delete a passport after the battery was recycled (Art. 77(8))",
        "description": "Only the economic operator's own clients with the scope dpp:delete, only after the recycling was recorded (UpdateDPPById with dpp:recycle, or in the panel); a recycling recorded by another company (a recycler) holds the deletion for 14 days (409 with `details.deletableFrom`), while the platform administrator can still reverse a wrong record. Deletes every version, its backup copies and the readings; the permanent address then answers 410 Gone and is never used for another battery. Notify the EU registry of the deletion.",
        "security": [
          {
            "oauth2": [
              "dpp:delete"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dppId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                    "deleted": true,
                    "versions": 4
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          }
        }
      }
    },
    "/api/dpp/v1/dpps/{dppId}/versions": {
      "get": {
        "operationId": "ReadDPPVersionByIdAndDate",
        "summary": "ReadDPPVersionByIdAndDate – the version valid at a timestamp",
        "security": [
          {},
          {
            "oauth2": [
              "dpp:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "DPP version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dpp": {
                      "dppHeader": {
                        "digitalProductPassportId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                        "dppStatus": {
                          "dppStatusValue": "active"
                        }
                      },
                      "performanceAndDurability": {
                        "stateOfCharge": {
                          "percentageValue": 80,
                          "percentUnit": "%"
                        }
                      }
                    },
                    "version": {
                      "versionNo": 3,
                      "publishedAt": "2026-10-01T08:00:00.000Z",
                      "sha256": "9f2c…",
                      "signature": "base64…"
                    },
                    "accessLevel": "legitimateInterest",
                    "links": {
                      "predecessors": [],
                      "successors": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          }
        }
      }
    },
    "/api/dpp/v1/dpps/{dppId}/elements/{elementPath}": {
      "get": {
        "operationId": "ReadDataElement",
        "summary": "ReadDataElement – one data element (section.attribute[.part])",
        "security": [
          {},
          {
            "oauth2": [
              "dpp:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          },
          {
            "$ref": "#/components/parameters/elementPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Element",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dppId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                    "elementPath": "performanceAndDurability.stateOfCharge",
                    "value": {
                      "percentageValue": 80,
                      "percentUnit": "%"
                    },
                    "versionNo": 3
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          }
        }
      },
      "patch": {
        "operationId": "UpdateDataElement",
        "summary": "UpdateDataElement – set or remove (value: null) one attribute; creates a new version",
        "security": [
          {
            "oauth2": [
              "dpp:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          },
          {
            "$ref": "#/components/parameters/elementPath"
          },
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "value": {
                  "percentageValue": 64
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated element",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          }
        },
        "description": "At most one automatic version a day per passport: a later change is stored and published when the day is over (the response gives publishAfter); the battery status, the putting into service and accidents are published at once."
      }
    },
    "/api/dpp/v1/dpps/search": {
      "post": {
        "operationId": "ReadDPPIdsByProductIds",
        "summary": "ReadDPPIdsByProductIds – DPP IDs for up to 100 Product IDs (identifiers only)",
        "security": [
          {
            "oauth2": [
              "dpp:search"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productIds"
                ],
                "properties": {
                  "productIds": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100
                  },
                  "cursor": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              },
              "example": {
                "productIds": [
                  "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123"
                ],
                "limit": 50
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "DPP IDs; cursor present when more results exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "statusCode": "Success",
                  "payload": {
                    "dppIds": [
                      "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123"
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          }
        }
      }
    },
    "/api/dpp/v1/dpps/{dppId}/readings": {
      "post": {
        "operationId": "AddReadings",
        "summary": "AddReadings – store dynamic data readings (platform extension, not an EN 18222 method); optionally apply the latest values as a new version",
        "description": "Paths: item-level attributes with dynamic data (state of charge, remaining capacity, cycles, temperature…) within the client's write paths. Readings do not create a version unless apply is true. Only active DPPs. With apply, at most one automatic version a day per passport: a later apply stores the values and answers with publishAfter, when the maintenance publishes them.",
        "security": [
          {
            "oauth2": [
              "dpp:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/dppId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "readings": [
                  {
                    "path": "performanceAndDurability.stateOfCharge",
                    "value": {
                      "percentageValue": 76
                    },
                    "measuredAt": "2026-10-01T08:00:00Z",
                    "source": "bms"
                  },
                  {
                    "path": "performanceAndDurability.numberOfFullChargingAndDischargingCycles",
                    "value": 312,
                    "measuredAt": "2026-10-01T08:00:00Z",
                    "source": "bms"
                  }
                ],
                "apply": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored",
            "content": {
              "application/json": {
                "example": {
                  "statusCode": "SuccessCreated",
                  "payload": {
                    "dppId": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                    "created": 2,
                    "versionNo": 4
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://batterycompliance.eu/oauth/token",
            "scopes": {
              "dpp:read": "Read DPP data within the client's access level",
              "dpp:search": "Discover DPP IDs by Product IDs",
              "dpp:write": "Update permitted item-level data elements",
              "dpp:recycle": "Record that the battery was recycled (Art. 77(8))",
              "dpp:delete": "Delete a passport after recycling (the operator’s own clients)"
            }
          }
        }
      }
    },
    "parameters": {
      "dppId": {
        "name": "dppId",
        "in": "path",
        "required": true,
        "description": "URL-encoded DPP ID (permanent URI)",
        "schema": {
          "type": "string"
        },
        "example": "https%3A%2F%2Fbatterycompliance.eu%2F01%2F03850012345678%2F21%2FKS16X-2026-000123"
      },
      "elementPath": {
        "name": "elementPath",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "performanceAndDurability.stateOfCharge"
      },
      "idempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 200
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "statusCode"
        ],
        "properties": {
          "statusCode": {
            "type": "string",
            "enum": [
              "Success",
              "SuccessCreated",
              "SuccessAccepted",
              "SuccessNoContent",
              "ClientErrorBadRequest",
              "ClientNotAuthorized",
              "ClientForbidden",
              "ClientMethodNotAllowed",
              "ClientErrorResourceNotFound",
              "ClientResourceConflict",
              "ServerInternalError",
              "ServerNotImplemented",
              "ServerErrorBadGateway"
            ]
          },
          "payload": {},
          "result": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string"
              },
              "details": {}
            }
          }
        }
      }
    },
    "responses": {
      "E400": {
        "description": "Malformed or invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "statusCode": "ClientErrorBadRequest",
              "result": {
                "message": "productIds must be a non-empty array"
              }
            }
          }
        }
      },
      "E401": {
        "description": "Missing, invalid, expired or revoked access token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "statusCode": "ClientNotAuthorized",
              "result": {
                "message": "Invalid access token"
              }
            }
          }
        }
      },
      "E403": {
        "description": "No access to the DPP, element or operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "statusCode": "ClientForbidden",
              "result": {
                "message": "No write access to these data elements"
              }
            }
          }
        }
      },
      "E404": {
        "description": "DPP or element does not exist",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "statusCode": "ClientErrorResourceNotFound",
              "result": {
                "message": "No DPP with this identifier"
              }
            }
          }
        }
      },
      "E409": {
        "description": "Status does not allow the update, or Idempotency-Key reuse",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            },
            "example": {
              "statusCode": "ClientResourceConflict",
              "result": {
                "message": "The DPP status is archived; only active DPPs can be updated"
              }
            }
          }
        }
      }
    }
  }
}
