openapi: 3.1.0
info:
  title: Battery DPP Lifecycle and Identity API
  version: 1.1.0
  description: EN 18222:2026 Lifecycle API reference implementation with JWT user authentication. Test Adapter routes are test-only.
servers:
  - url: http://127.0.0.1:3001
security:
  - bearerAuth: []
paths:
  /auth/register:
    post:
      operationId: RegisterUser
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UserRegistration' }
      responses:
        '201': { description: User registered and JWT issued }
        '409': { description: Email already registered }
  /auth/login:
    post:
      operationId: Login
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/LoginRequest' }
      responses:
        '200': { description: Authenticated user and JWT access token }
        '401': { $ref: '#/components/responses/Unauthorized' }
  /auth/me:
    get:
      operationId: ReadCurrentUser
      responses:
        '200': { description: Current authenticated user }
        '401': { $ref: '#/components/responses/Unauthorized' }
    patch:
      operationId: UpdateCurrentUser
      responses:
        '200': { description: Current user updated }
  /auth/logout:
    post:
      operationId: Logout
      responses:
        '204': { description: Current JWT revoked }
  /ui-api/users:
    get:
      operationId: ListUsers
      responses:
        '200': { description: User list }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: CreateUser
      responses:
        '201': { description: User created }
        '403': { $ref: '#/components/responses/Forbidden' }
  /ui-api/users/{userId}:
    parameters:
      - name: userId
        in: path
        required: true
        schema: { type: string, format: uuid }
    patch:
      operationId: UpdateUser
      responses: { '200': { description: User updated } }
    delete:
      operationId: DisableUser
      responses: { '204': { description: User disabled and tokens revoked } }
  /v1/dpps:
    post:
      operationId: CreateDPP
      security: [{ bearerAuth: [] }, { apiKeyAuth: [] }]
      responses:
        '201': { description: DPP created }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { description: DPP identifier already exists }
  /v1/dpps/{dppId}:
    parameters: [{ $ref: '#/components/parameters/DppId' }]
    get:
      operationId: ReadDPPById
      responses:
        '200':
          description: Raw DPP JSON document
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DPPDocument' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      operationId: UpdateDPPById
      parameters: [{ $ref: '#/components/parameters/IfMatch' }]
      responses:
        '200': { description: Updated DPP }
        '400': { $ref: '#/components/responses/BadRequest' }
        '409': { description: ETag conflict }
    delete:
      operationId: DeleteDPPById
      responses:
        '204': { description: DPP retired }
  /v1/dppsByProductId/{productId}:
    get:
      operationId: ReadDPPByProductId
      parameters:
        - name: productId
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Current raw DPP JSON document
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DPPDocument' }
        '404': { $ref: '#/components/responses/NotFound' }
  /v1/dppsByIdAndDate/{dppId}:
    get:
      operationId: ReadDPPVersionByIdAndDate
      parameters:
        - $ref: '#/components/parameters/DppId'
        - name: date
          in: query
          required: true
          schema: { type: string, format: date-time }
      responses:
        '200': { description: DPP version }
  /v1/dppsByProductIds:
    post:
      operationId: ReadDPPIdsByProductIds
      responses:
        '200': { description: Matching DPP identifiers }
  /v1/dpps/{dppId}/elements/{elementIdPath}:
    parameters:
      - $ref: '#/components/parameters/DppId'
      - name: elementIdPath
        in: path
        required: true
        schema:
          type: string
          description: Percent-encoded RFC 9535 JSONPath resolving to exactly one element (including a deterministic wildcard), RFC 6901 JSON Pointer, dotted path, or unique data-element identifier
    get:
      operationId: ReadDataElement
      responses:
        '200':
          description: A JSON subdocument keyed by the resolved data-element name
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DataElementDocument' }
    patch:
      operationId: UpdateDataElement
      parameters: [{ $ref: '#/components/parameters/IfMatch' }]
      responses:
        '200':
          description: The updated JSON subdocument keyed by the resolved data-element name
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DataElementDocument' }
components:
  parameters:
    DppId:
      name: dppId
      in: path
      required: true
      schema: { type: string }
    IfMatch:
      name: If-Match
      in: header
      required: false
      schema: { type: string }
  responses:
    Unauthorized: { description: Missing, expired, revoked or invalid Bearer token }
    BadRequest: { description: Invalid JSON, parameter or Schema validation failure }
    Forbidden: { description: Missing role or field-level access right }
    NotFound: { description: DPP or data element not found }
  securitySchemes:
    bearerAuth: { type: http, scheme: bearer, bearerFormat: JWT }
    apiKeyAuth: { type: apiKey, in: header, name: X-API-Key }
  schemas:
    DataElementDocument:
      type: object
      minProperties: 1
      maxProperties: 1
      additionalProperties: true
      example:
        EconomicOperatorInformation:
          name: VoltCore_GmbH
    DPPDocument:
      type: object
      required: [Battery_Passport]
      properties:
        Battery_Passport: { type: object, additionalProperties: true }
      additionalProperties: false
    UserRegistration:
      type: object
      required: [name, email, password]
      properties:
        name: { type: string, minLength: 2, maxLength: 80 }
        email: { type: string, format: email }
        password: { type: string, minLength: 8, maxLength: 128, writeOnly: true }
    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email: { type: string, format: email }
        password: { type: string, writeOnly: true }
