{
  "openapi": "3.1.0",
  "info": {
    "title": "Battery Compliance internal API",
    "version": "1.0.0",
    "description": "List, import, update and publish battery items (digital battery passports under Regulation (EU) 2023/1542). Authenticate with an API key created by the company owner: `Authorization: Bearer bcp_live_…`. Limit: 60 requests per minute per key. Errors: `{ \"error\": { \"code\", \"message\", \"details\"? } }`. Plan limits answer 403 with `details.code`: `no_subscription` (the company has no subscription), `expired` (the paid plan ended or stopped for late payment – the limits of Free apply; `limit` is Free's), `models_limit`, `items_limit`, `items_unlocked` (monthly Business unlocks a twelfth of its passports per paid month), `open_items_limit` (a manufacturer outside the EU above its cap of open items – drafts and batteries in batches not confirmed yet), `files_quota` (no room left in the company's files; `limit`, `used`, `remaining` in bytes)."
  },
  "servers": [
    {
      "url": "https://batterycompliance.eu"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/api/v1/models": {
      "get": {
        "summary": "List the company's models",
        "description": "Scope: items:read or items:write. Sorted by model code. Pages: pass `nextCursor` of the response as `cursor` until it is null.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                },
                "example": {
                  "models": [
                    {
                      "id": "cmu4a0z9y0000model1234abc",
                      "code": "KS-16X",
                      "name": "KS-16X Pack 1500 Wh",
                      "category": "LMT",
                      "status": "published",
                      "gtin": "03850012345678",
                      "schemaVersion": "2.0.0",
                      "createdAt": "2026-09-16T09:00:00.000Z",
                      "updatedAt": "2026-09-18T12:30:00.000Z"
                    }
                  ],
                  "nextCursor": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/duplicate": {
      "post": {
        "summary": "Copy a model with its documents as a new draft",
        "description": "Scope: items:write. The model attributes and own copies of the documents (links in the attributes point to the copies); items and versions are not copied. GTIN required for operators with GS1 identifiers, within the verified company prefix. The copies of the documents count towards the company's files: a manufacturer outside the EU keeps up to 2 GB, otherwise 403 with `details.code = files_quota` (`limit`, `used`, `remaining` in bytes) and nothing is copied.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DuplicateModelRequest"
              },
              "example": {
                "code": "KS-16X-1800",
                "name": "KS-16X Pack 1800 Wh",
                "gtin": "03850012345685"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "id": "cmu7q2d3e0000copy1234abcd",
                  "code": "KS-16X-1800",
                  "name": "KS-16X Pack 1800 Wh",
                  "gtin": "03850012345685",
                  "category": "LMT",
                  "status": "draft",
                  "documents": 4
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/items": {
      "get": {
        "summary": "List the items of a model",
        "description": "Scope: items:read or items:write. Sorted by serial number, up to 1000 per page. Pages: pass `nextCursor` of the response as `cursor` until it is null; every item is returned once, also when items are added meanwhile. Synchronisation: `updatedSince` returns only items created or changed at or after that time (store the time before you start and use it next time).",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "name": "updatedSince",
            "in": "query",
            "description": "RFC 3339 date-time with a time zone, e.g. 2026-09-21T08:00:00Z.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Items",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemList"
                },
                "example": {
                  "items": [
                    {
                      "id": "cmu4a1b2c0000abcd1234efgh",
                      "serial": "KS16X-2026-000123",
                      "uri": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123",
                      "status": "original",
                      "dppStatus": "active",
                      "manufacturingDate": "2026-08",
                      "putIntoServiceDate": null,
                      "facilityCode": "SOF1",
                      "attributes": {
                        "performanceAndDurability": {
                          "stateOfCharge": {
                            "percentageValue": 80
                          }
                        }
                      },
                      "latestVersion": {
                        "versionNo": 2,
                        "publishedAt": "2026-09-18T12:31:04.512Z"
                      },
                      "changedSincePublication": false,
                      "transferredTo": null,
                      "createdAt": "2026-09-16T09:05:00.000Z",
                      "updatedAt": "2026-09-18T12:31:04.530Z"
                    }
                  ],
                  "nextCursor": "S1MxNlgtMjAyNi0wMDAxMjM"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "summary": "Create items (all-or-nothing, up to 1000 per request)",
        "description": "Scope: items:write. Items are created with a permanent URI (GS1 Digital Link or own scheme). Plan limit: 403 with `details.code = items_limit | items_unlocked | expired | no_subscription`, or `open_items_limit` for a manufacturer outside the EU (it prepares free on every plan up to its cap of open items; its passports are counted when its importers confirm their batches).",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateItemsRequest"
              },
              "example": {
                "items": [
                  {
                    "serial": "KS16X-2026-000123",
                    "manufacturingDate": "2026-08",
                    "facilityCode": "SOF1",
                    "status": "original",
                    "attributes": {
                      "performanceAndDurability": {
                        "stateOfCharge": {
                          "percentageValue": 80,
                          "percentUnit": "%"
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateItemsResponse"
                },
                "example": {
                  "created": [
                    {
                      "id": "cmu4a1b2c0000abcd1234efgh",
                      "serial": "KS16X-2026-000123",
                      "uri": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123"
                    }
                  ],
                  "errors": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/items/range": {
      "post": {
        "summary": "Create a range of items with consecutive serial numbers (all-or-nothing, up to 20,000)",
        "description": "Scope: items:write. Serial numbers are prefix + the number padded to `digits` + suffix, e.g. KS16X-2026-00001 … KS16X-2026-05000. Existing serial numbers → 409 with their count and examples; nothing is created. Plan limit: as for POST /items – 403 with `details.code = items_limit | items_unlocked | expired | no_subscription | open_items_limit`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRangeRequest"
              },
              "example": {
                "prefix": "KS16X-2026-",
                "start": 1,
                "count": 5000,
                "digits": 5,
                "manufacturingDate": "2026-09",
                "facilityCode": "SOF1",
                "status": "original"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateRangeResponse"
                },
                "example": {
                  "created": 5000,
                  "first": "KS16X-2026-00001",
                  "last": "KS16X-2026-05000"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/import-batches": {
      "post": {
        "summary": "Assign batteries of a model to an importer as a batch (manufacturer outside the EU)",
        "description": "Scope: items:publish. The operator that places the battery on the EU market pays (docs/35): the passports of a batch count in full in the payer's plan when the importer confirms the batch – the importer, unless the manufacturer pays. Who pays is the link's choice (Company → Importers; the importer by default) and is recorded on the batch when it is assigned; it does not change after the confirmation. `billedTo: \"manufacturer\"` makes the manufacturer pay for this batch, as \"Pay for this batch\" in the panel: its plan must have room for the batch's passports, otherwise 403 with `details.code = plan_limit` (`items`, `limit`, `used`, `unlocked`) and nothing is assigned. Only the importer confirms the batch, in its panel (there is no API for it); then it is the responsible economic operator in the passports and they are published – the manufacturer never publishes them by itself. The confirmation is refused with `link_pending` until the importer has accepted the link (its written authorisation, Art. 77(4)), with `brand_declaration` without the declaration that the batteries are placed on the market under the manufacturer's name or trademark and are not modified, and with `plan_limit` when the payer's plan has no room – the batch then waits and the manufacturer is told by e-mail. The link must be active, or the importer one not on the platform yet (entered in Company → Importers and invited by e-mail): the batch waits until it registers, accepts the link and confirms. Until then the batch can be withdrawn in the panel. Registering the batch through the EU registry's API on the importer's behalf needs a paid plan of the payer in force (otherwise `payer_plan`: prepare the registry file, which works on every plan).",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssignBatchRequest"
              },
              "example": {
                "importer": "BG206000000",
                "range": {
                  "from": "KS16X-2026-00001",
                  "to": "KS16X-2026-00500"
                },
                "code": "CONT-MSKU1234567",
                "expectedArrival": "2026-11-20",
                "billedTo": "manufacturer"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Assigned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssignBatchResponse"
                },
                "example": {
                  "id": "cm…",
                  "code": "CONT-MSKU1234567",
                  "status": "assigned",
                  "items": 500,
                  "billedTo": "manufacturer",
                  "skipped": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/import-batches/{batchId}": {
      "get": {
        "summary": "A batch for an importer with its serial numbers (the manufacturer's or the importer's key)",
        "description": "Scope: items:read. `billedTo` – who pays for the passports of the batch: the importer, or the manufacturer; they count in the payer's plan when the importer confirms the batch (`status = confirmed`).",
        "parameters": [
          {
            "$ref": "#/components/parameters/batchId"
          }
        ],
        "responses": {
          "200": {
            "description": "The batch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportBatch"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/publish-jobs": {
      "post": {
        "summary": "Publish items of a model in the background",
        "description": "Scope: items:publish. Publishes the not yet published items (includeUnpublished) and/or those changed since their last version (includeChanged); both by default, the body may be empty. One queued or running job per model (409). Poll the job with GET until status is done or failed. For a manufacturer outside the EU only the batteries of batches its importers confirmed are candidates; without such batteries 422 (nothing to publish). A job started through the API publishes like POST /items/{itemId}/publish: at most one version a day per passport from the company's systems – a later change waits and is published when the day is over (counted in `deferred`); a first publication is never held back.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublishJobRequest"
              },
              "example": {
                "includeUnpublished": true,
                "includeChanged": true
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted; Location header points to the job",
            "content": {
              "application/json": {
                "example": {
                  "id": "cmu7p0b1c0000pjob1234abcd",
                  "status": "queued",
                  "total": 5000
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/publish-jobs/{jobId}": {
      "get": {
        "summary": "Progress of a publishing job",
        "description": "Scope: items:publish. `failures` lists the first 1000 items that could not be published (incomplete or invalid data).",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          },
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishJob"
                },
                "example": {
                  "id": "cmu7p0b1c0000pjob1234abcd",
                  "status": "done",
                  "total": 5000,
                  "published": 4997,
                  "deferred": 0,
                  "failed": 3,
                  "failures": [
                    {
                      "serial": "KS16X-2026-00042",
                      "code": "incomplete",
                      "missing": 2
                    }
                  ],
                  "error": null,
                  "createdAt": "2026-09-18T10:00:00.000Z",
                  "startedAt": "2026-09-18T10:00:00.120Z",
                  "finishedAt": "2026-09-18T10:00:41.700Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/items/{itemId}": {
      "patch": {
        "summary": "Update an item",
        "description": "Scope: items:write. `attributes` are merged into the existing item attributes; only item-level fields of the catalogue are accepted.",
        "parameters": [
          {
            "$ref": "#/components/parameters/itemId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateItemRequest"
              },
              "example": {
                "status": "repurposed",
                "putIntoServiceDate": "2026-10",
                "attributes": {
                  "performanceAndDurability": {
                    "stateOfCharge": {
                      "percentageValue": 64
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated item",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Item"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/items/{itemId}/publish": {
      "post": {
        "summary": "Publish a new passport version",
        "description": "Scope: items:publish. Validates against the JSON schema and the category profile; missing fields mandatory from 02/2027 block publication (422). A new version is made only when the content changed. At most one automatic version (API key or OAuth client) a day per passport: a later change is stored and published when the day is over – the response gives publishAfter (docs/32, task 118). 200 with unchanged: true when nothing changed; 202 with publishAfter when the change waits. A manufacturer outside the EU never publishes by itself (docs/35): 409 with `details.code = importer_required` when the battery is in no batch, `import_pending` when its batch is not confirmed by the importer yet. A battery of a confirmed batch was counted at the confirmation and is not counted again. First publication of a draft above the plan in force: 403 with `details.code = items_limit`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/itemId"
          }
        ],
        "responses": {
          "201": {
            "description": "Published",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishResponse"
                },
                "example": {
                  "versionNo": 2,
                  "sha256": "9f2c…",
                  "url": "https://batterycompliance.eu/01/03850012345678/21/KS16X-2026-000123?version=2",
                  "backupConfirmed": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "description": "Not publishable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unprocessable",
                    "message": "Mandatory fields are missing",
                    "details": {
                      "missing": [
                        "identifiersAndProductData.batteryMass"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/items/{itemId}/qr": {
      "get": {
        "summary": "QR code of the item URI",
        "description": "Scope: qr:read. Error correction level M. Recommended minimum printed size 15 × 15 mm.",
        "parameters": [
          {
            "$ref": "#/components/parameters/itemId"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "svg",
                "png"
              ],
              "default": "svg"
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "PNG width in pixels (64–2048)",
            "schema": {
              "type": "integer",
              "default": 512
            }
          },
          {
            "name": "caption",
            "in": "query",
            "description": "1 – adds \"Battery passport\" below the code (not required by Annex VI Part C; tells the holder what the code opens)",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "QR code",
            "content": {
              "image/svg+xml": {},
              "image/png": {}
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/documents": {
      "get": {
        "summary": "Generated documents of the operator",
        "description": "Scope: documents:read. Documents issued by the compliance assistants (labels, declarations, policies, reports) with their signed versions. Public kinds also have a verification page at verifyUrl.",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "description": "One of the document kinds",
            "schema": {
              "type": "string",
              "enum": [
                "label_template",
                "capacity_label",
                "carbon_label",
                "transport_label",
                "packaging_label",
                "eu_doc",
                "packaging_doc",
                "carbon_declaration",
                "dd_policy",
                "dd_report",
                "dossier_manifest",
                "packaging_report"
              ]
            }
          },
          {
            "name": "modelId",
            "in": "query",
            "description": "Documents of one battery model",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Documents",
            "content": {
              "application/json": {
                "example": {
                  "documents": [
                    {
                      "id": "cmgd1",
                      "kind": "eu_doc",
                      "title": "EU declaration of conformity – KS-16X",
                      "modelId": "cmmodel1",
                      "labelProductId": null,
                      "variant": "",
                      "createdAt": "2026-09-23T10:00:00.000Z",
                      "versions": [
                        {
                          "versionNo": 1,
                          "issuedAt": "2026-09-23T10:00:00.000Z",
                          "sha256": "9f86…",
                          "keyId": "batterycompliance-2026-1",
                          "pdfSize": 48213,
                          "verifyUrl": "https://batterycompliance.eu/verify/document/cmgd1/1",
                          "modelDocumentId": "cmmd1",
                          "backedUp": true
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/documents/{documentId}/{versionNo}": {
      "get": {
        "summary": "One version of a generated document: signed data or PDF",
        "description": "Scope: documents:read. JSON with the signed envelope (data), its SHA-256 (over the canonical JSON, RFC 8785), the Ed25519 signature and the key id. With format=pdf (or Accept: application/pdf) the PDF is returned; its metadata and footer carry the same hash and key.",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "versionNo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "pdf"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Document version",
            "content": {
              "application/json": {
                "example": {
                  "document": {
                    "id": "cmgd1",
                    "kind": "eu_doc",
                    "title": "EU declaration of conformity – KS-16X",
                    "modelId": "cmmodel1",
                    "labelProductId": null,
                    "variant": ""
                  },
                  "version": {
                    "versionNo": 1,
                    "issuedAt": "2026-09-23T10:00:00.000Z",
                    "sha256": "9f86…",
                    "signature": "base64…",
                    "keyId": "batterycompliance-2026-1",
                    "pdfSha256": "ab12…",
                    "pdfSize": 48213,
                    "verifyUrl": "https://batterycompliance.eu/verify/document/cmgd1/1",
                    "modelDocumentId": "cmmd1",
                    "backedUp": true,
                    "data": {
                      "kind": "eu_doc",
                      "documentId": "cmgd1",
                      "versionNo": 1,
                      "issuedAt": "2026-09-23T10:00:00Z",
                      "title": "EU declaration of conformity – KS-16X",
                      "operator": {
                        "name": "Kingsong Europe EOOD",
                        "eoCode": "BG206000000"
                      },
                      "subject": {
                        "type": "model",
                        "code": "KS-16X"
                      },
                      "content": {}
                    }
                  }
                }
              },
              "application/pdf": {}
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/label-templates": {
      "get": {
        "summary": "Issued label templates of a model",
        "description": "Scope: documents:read or items:read. The general label (Annex VI Part A), the capacity label (Art. 13(2)), the carbon footprint class label (Art. 7) and the transport labels (one per shipment mode, `mode`: alone – UN 3480/3090, in_equipment – UN 3481/3091 packed in equipment, with_equipment – packed with equipment) issued by the label assistant, with their versions and file addresses. Use the file addresses as listed: those of a transport label carry its mode.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "responses": {
          "200": {
            "description": "Templates",
            "content": {
              "application/json": {
                "example": {
                  "templates": [
                    {
                      "id": "cmgd2",
                      "kind": "label_template",
                      "title": "Battery label – KS-16X",
                      "versions": [
                        {
                          "versionNo": 1,
                          "issuedAt": "2026-09-23T10:00:00.000Z",
                          "sha256": "9f86…",
                          "keyId": "batterycompliance-2026-1",
                          "verifyUrl": "https://batterycompliance.eu/verify/document/cmgd2/1",
                          "files": [
                            "/api/v1/models/cmmodel1/label-templates/1.svg?kind=label_template",
                            "/api/v1/models/cmmodel1/label-templates/1.pdf?kind=label_template",
                            "/api/v1/models/cmmodel1/label-templates/1.png?kind=label_template"
                          ]
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/label-templates/{file}": {
      "get": {
        "summary": "SVG, PDF or PNG of an issued label version",
        "description": "Scope: documents:read or items:read. `{file}` is `{versionNo}.svg`, `{versionNo}.pdf` (the stored sample) or `{versionNo}.png` (600 dpi by default). With `serial` the general label is rendered for that item (serial number, month/year, its passport QR).",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          },
          {
            "name": "file",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1.svg"
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "label_template",
                "capacity_label",
                "carbon_label",
                "transport_label"
              ],
              "default": "label_template"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "A transport label: the shipment mode it was issued for (the `mode` of the template in the list)",
            "schema": {
              "type": "string",
              "enum": [
                "alone",
                "in_equipment",
                "with_equipment"
              ],
              "default": "alone"
            }
          },
          {
            "name": "serial",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "batch",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dpi",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 600
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Label file",
            "content": {
              "image/svg+xml": {},
              "application/pdf": {},
              "image/png": {}
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/items/labels.pdf": {
      "get": {
        "summary": "Sheets of item labels from an issued template",
        "description": "Scope: items:read or documents:read. Each item gets the general label of template version `template` with its serial number, month/year and passport QR; on A4 unless `paper` chooses another sheet or a custom size (`orientation=landscape` turns it); as many labels per sheet as fit inside 8 mm margins, or the grid of `columns`, `rows`, `marginTopMm`, `marginLeftMm`, `gapXMm`, `gapYMm` (400 if it does not fit the sheet). Up to 5 000 labels per file.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          },
          {
            "name": "template",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Version number of the issued general label template"
          },
          {
            "name": "prefix",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Serial number starts with"
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            },
            "description": "Position in the list sorted by serial number"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 5000
            }
          },
          {
            "name": "batch",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Value of the {batch} field"
          },
          {
            "name": "paper",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "A4",
                "A5",
                "A3",
                "Letter",
                "Legal",
                "custom"
              ],
              "default": "A4"
            },
            "description": "Sheet size; `custom` takes paperWidthMm and paperHeightMm"
          },
          {
            "name": "orientation",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "portrait",
                "landscape"
              ],
              "default": "portrait"
            },
            "description": "Landscape swaps the sheet's width and height"
          },
          {
            "name": "paperWidthMm",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 20,
              "maximum": 1200
            },
            "description": "Width of a custom sheet (mm)"
          },
          {
            "name": "paperHeightMm",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 20,
              "maximum": 1200
            },
            "description": "Height of a custom sheet (mm)"
          },
          {
            "name": "columns",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Labels per row (with a grid; 1–20)"
          },
          {
            "name": "rows",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Labels per column (with a grid; 1–40)"
          },
          {
            "name": "marginTopMm",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "description": "Top margin of the grid (mm)"
          },
          {
            "name": "marginLeftMm",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "description": "Left margin of the grid (mm)"
          },
          {
            "name": "gapXMm",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "description": "Horizontal gap between labels (mm)"
          },
          {
            "name": "gapYMm",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0
            },
            "description": "Vertical gap between labels (mm)"
          }
        ],
        "responses": {
          "200": {
            "description": "PDF",
            "content": {
              "application/pdf": {}
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/models/{modelId}/packaging": {
      "get": {
        "summary": "Packaging specification of a model (Regulation (EU) 2025/40)",
        "description": "Scope: packaging:read or packaging:write. Components, volumes, summary by material and the checks (problem / attention / information, each with the article and the date it applies from). See docs/24.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "responses": {
          "200": {
            "description": "Specification",
            "content": {
              "application/json": {
                "example": {
                  "status": "issued",
                  "version": 1,
                  "issuedAt": "2026-09-23T10:00:00.000Z",
                  "packagingVolumeCm3": 4000,
                  "productVolumeCm3": 2600,
                  "minimisationJustified": true,
                  "minimisationNote": "Annex IV assessed",
                  "designForRecyclingNote": null,
                  "components": [
                    {
                      "level": "sales",
                      "type": "box",
                      "material": "paperCardboard",
                      "materialCode": "PAP 20",
                      "massG": 180,
                      "recycledContentPct": 80,
                      "reusable": false,
                      "heavyMetalsDeclared": true,
                      "supplier": "Cartons Ltd",
                      "inksAdhesivesNote": null,
                      "note": null,
                      "id": "cmpc1"
                    }
                  ],
                  "summary": {
                    "totalMassG": 180,
                    "byMaterial": [
                      {
                        "material": "paperCardboard",
                        "massG": 180,
                        "share": 100
                      }
                    ],
                    "byLevel": [
                      {
                        "level": "sales",
                        "massG": 180
                      }
                    ],
                    "reusableMassG": 0,
                    "recycledMassG": 144
                  },
                  "checks": [
                    {
                      "code": "recyclability",
                      "severity": "info",
                      "article": "Art. 6",
                      "appliesFrom": "2030-01-01"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "summary": "Update the packaging specification",
        "description": "Scope: packaging:write; feature packaging (Business/Enterprise). Volumes and notes are merged; `components` replaces all components (up to 10 000). The specification returns to draft.",
        "parameters": [
          {
            "$ref": "#/components/parameters/modelId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "packagingVolumeCm3": 4000,
                "productVolumeCm3": 2600,
                "minimisationJustified": true,
                "components": [
                  {
                    "level": "sales",
                    "type": "box",
                    "material": "paperCardboard",
                    "materialCode": "PAP 20",
                    "massG": 180,
                    "recycledContentPct": 80,
                    "reusable": false,
                    "heavyMetalsDeclared": true,
                    "supplier": "Cartons Ltd",
                    "inksAdhesivesNote": null,
                    "note": null
                  },
                  {
                    "level": "transport",
                    "type": "film",
                    "material": "plastic",
                    "materialCode": "LDPE 4",
                    "massG": 25,
                    "heavyMetalsDeclared": true
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Specification after the update (same shape as GET)"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/registrations": {
      "get": {
        "summary": "Producer registrations by country",
        "description": "Scope: registrations:read. The markets of the company, its registrations as a producer of batteries (Art. 55) and packaging (Art. 44–45 of Regulation (EU) 2025/40) by country, with the status applied to the validity date, and the warnings (markets without registration, expiring within 90 days, expired). Not the EU battery passport registry.",
        "responses": {
          "200": {
            "description": "Registrations",
            "content": {
              "application/json": {
                "example": {
                  "markets": [
                    "BG",
                    "DE"
                  ],
                  "registrations": [
                    {
                      "id": "cmr1",
                      "country": "DE",
                      "countryName": "Germany",
                      "scheme": "batteries",
                      "status": "registered",
                      "registrationNumber": "WEEE-Reg.-Nr. DE 12345678",
                      "organisation": "stiftung ear",
                      "representativeName": null,
                      "representativeCountry": null,
                      "validUntil": "2027-06-30",
                      "documentUrl": null,
                      "note": null,
                      "updatedAt": "2026-09-23T10:00:00.000Z"
                    }
                  ],
                  "warnings": {
                    "marketsWithoutRegistration": [
                      {
                        "country": "DE",
                        "scheme": "packaging"
                      }
                    ],
                    "expiring": [],
                    "expired": []
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/verify": {
      "post": {
        "summary": "Bulk verification of passports",
        "description": "Scope: verify:read; feature verify_bulk (Business and Enterprise). For importers and distributors (Art. 41–42): up to 10 000 permanent passport addresses of any operator on the platform, or serial numbers together with a model address. For each: status (published, archived, suspended, deleted 410, moved, not_found, invalid), the latest version and its date, the model code and whether the model has an issued label template and EU declaration of conformity – facts that are public on the passport page. Drafts never published count as not found. The request is one entry in the access log (with the count) and a run in the operator's history. Body: JSON, or text/csv (first column; model address in ?model=). 200 checks per operator and hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "items": [
                  "https://batterycompliance.eu/01/04012345678901/21/SN0001",
                  "SN0002"
                ],
                "model": "https://batterycompliance.eu/01/04012345678901"
              }
            },
            "text/csv": {
              "example": "uri\nhttps://batterycompliance.eu/01/04012345678901/21/SN0001\n"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Results in the order of the input",
            "content": {
              "application/json": {
                "example": {
                  "runId": "cmv1",
                  "summary": {
                    "total": 2,
                    "found": 1,
                    "published": 1,
                    "archived": 0,
                    "suspended": 0,
                    "deleted": 0,
                    "moved": 0,
                    "notFound": 1,
                    "invalid": 0
                  },
                  "results": [
                    {
                      "input": "https://batterycompliance.eu/01/04012345678901/21/SN0001",
                      "uri": "https://batterycompliance.eu/01/04012345678901/21/SN0001",
                      "status": "published",
                      "httpStatus": 200,
                      "versionNo": 3,
                      "publishedAt": "2026-09-23T08:00:00.000Z",
                      "model": "KS-16X",
                      "labelTemplate": true,
                      "euDeclaration": true
                    },
                    {
                      "input": "SN0002",
                      "uri": "https://batterycompliance.eu/01/04012345678901/21/SN0002",
                      "status": "not_found",
                      "httpStatus": 404,
                      "versionNo": null,
                      "publishedAt": null,
                      "model": null,
                      "labelTemplate": null,
                      "euDeclaration": null
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "bcp_live_…"
      }
    },
    "parameters": {
      "modelId": {
        "name": "modelId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "itemId": {
        "name": "itemId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "description": "Page size (default 100; items up to 1000, models up to 500).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 100
        }
      },
      "cursor": {
        "name": "cursor",
        "in": "query",
        "description": "`nextCursor` of the previous page.",
        "schema": {
          "type": "string"
        }
      },
      "batchId": {
        "name": "batchId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "ModelList": {
        "type": "object",
        "required": [
          "models",
          "nextCursor"
        ],
        "properties": {
          "models": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "category": {
                  "type": "string",
                  "enum": [
                    "EV",
                    "LMT",
                    "stationary",
                    "industrialOther",
                    "industrialWithoutBms"
                  ]
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "draft",
                    "published",
                    "archived"
                  ]
                },
                "gtin": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "schemaVersion": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "updatedAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ItemList": {
        "type": "object",
        "required": [
          "items",
          "nextCursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "serial": {
                  "type": "string"
                },
                "uri": {
                  "type": "string",
                  "format": "uri"
                },
                "status": {
                  "$ref": "#/components/schemas/BatteryStatus"
                },
                "dppStatus": {
                  "type": "string",
                  "enum": [
                    "draft",
                    "active",
                    "archived",
                    "suspended"
                  ]
                },
                "manufacturingDate": {
                  "$ref": "#/components/schemas/Month"
                },
                "putIntoServiceDate": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "facilityCode": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "attributes": {
                  "$ref": "#/components/schemas/ItemAttributes"
                },
                "latestVersion": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "properties": {
                    "versionNo": {
                      "type": "integer"
                    },
                    "publishedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "changedSincePublication": {
                  "type": "boolean",
                  "description": "Data changed after the latest published version."
                },
                "transferredTo": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "New address after a transfer to another provider (the permanent URI redirects there)."
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "updatedAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Month": {
        "type": "string",
        "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])$",
        "examples": [
          "2026-08"
        ]
      },
      "BatteryStatus": {
        "type": "string",
        "enum": [
          "original",
          "repurposed",
          "reused",
          "remanufactured",
          "waste"
        ]
      },
      "ItemAttributes": {
        "type": "object",
        "description": "Item-level passport attributes by section, as in https://batterycompliance.eu/schema/battery-passport/2.0.0",
        "additionalProperties": {
          "type": "object"
        }
      },
      "CreateItemsRequest": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "type": "object",
              "required": [
                "serial",
                "manufacturingDate"
              ],
              "properties": {
                "serial": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9._-]{1,50}$"
                },
                "manufacturingDate": {
                  "$ref": "#/components/schemas/Month"
                },
                "facilityCode": {
                  "type": "string"
                },
                "status": {
                  "$ref": "#/components/schemas/BatteryStatus"
                },
                "putIntoServiceDate": {
                  "$ref": "#/components/schemas/Month"
                },
                "attributes": {
                  "$ref": "#/components/schemas/ItemAttributes"
                }
              }
            }
          }
        }
      },
      "CreateItemsResponse": {
        "type": "object",
        "properties": {
          "created": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "serial": {
                  "type": "string"
                },
                "uri": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {}
          }
        }
      },
      "CreateRangeRequest": {
        "type": "object",
        "required": [
          "prefix",
          "start",
          "count",
          "digits",
          "manufacturingDate"
        ],
        "properties": {
          "prefix": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._-]{0,40}$"
          },
          "suffix": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._-]{0,40}$"
          },
          "start": {
            "type": "integer",
            "minimum": 0
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20000
          },
          "digits": {
            "type": "integer",
            "minimum": 1,
            "maximum": 12
          },
          "manufacturingDate": {
            "$ref": "#/components/schemas/Month"
          },
          "facilityCode": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "original",
              "waste"
            ],
            "description": "repurposed, reused and remanufactured are set through the link to the original passport (Art. 77(7))"
          },
          "putIntoServiceDate": {
            "$ref": "#/components/schemas/Month"
          }
        }
      },
      "CreateRangeResponse": {
        "type": "object",
        "properties": {
          "created": {
            "type": "integer"
          },
          "first": {
            "type": "string"
          },
          "last": {
            "type": "string"
          }
        }
      },
      "PublishJobRequest": {
        "type": "object",
        "properties": {
          "includeUnpublished": {
            "type": "boolean",
            "default": true
          },
          "includeChanged": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "PublishJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "failed"
            ]
          },
          "total": {
            "type": "integer"
          },
          "published": {
            "type": "integer"
          },
          "deferred": {
            "type": "integer",
            "description": "Of `published`: changes that wait for their day (a job started through the API publishes at most one version a day per passport); they are published when the day is over."
          },
          "failed": {
            "type": "integer"
          },
          "failures": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "serial": {
                  "type": "string"
                },
                "code": {
                  "type": "string",
                  "enum": [
                    "incomplete",
                    "invalid",
                    "not_found"
                  ]
                },
                "missing": {
                  "type": "integer"
                }
              }
            }
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DuplicateModelRequest": {
        "type": "object",
        "required": [
          "code",
          "name"
        ],
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._-]{1,50}$"
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "gtin": {
            "type": "string",
            "description": "GTIN-14 (or 8/12/13 digits, normalised)"
          }
        }
      },
      "UpdateItemRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "status": {
            "$ref": "#/components/schemas/BatteryStatus"
          },
          "putIntoServiceDate": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Month"
              },
              {
                "type": "null"
              }
            ]
          },
          "attributes": {
            "$ref": "#/components/schemas/ItemAttributes"
          }
        }
      },
      "Item": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "serial": {
            "type": "string"
          },
          "uri": {
            "type": "string",
            "format": "uri"
          },
          "status": {
            "$ref": "#/components/schemas/BatteryStatus"
          },
          "putIntoServiceDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "attributes": {
            "$ref": "#/components/schemas/ItemAttributes"
          },
          "dppStatus": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "archived",
              "suspended"
            ]
          }
        }
      },
      "PublishResponse": {
        "type": "object",
        "properties": {
          "versionNo": {
            "type": "integer"
          },
          "sha256": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "backupConfirmed": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "unprocessable",
                  "rate_limited",
                  "internal"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {}
            }
          }
        }
      },
      "AssignBatchRequest": {
        "type": "object",
        "required": [
          "importer"
        ],
        "description": "Exactly one of serials, range or allUnassigned.",
        "properties": {
          "importer": {
            "type": "string",
            "description": "The importer's operator code: an active link, or an importer not on the platform yet that the manufacturer invited by e-mail (Company → Importers)."
          },
          "serials": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 20000
          },
          "range": {
            "type": "object",
            "required": [
              "from",
              "to"
            ],
            "properties": {
              "from": {
                "type": "string"
              },
              "to": {
                "type": "string"
              }
            },
            "description": "Serial numbers compared as text, both included."
          },
          "allUnassigned": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "code": {
            "type": "string",
            "maxLength": 64,
            "description": "Batch code; default {model}-{YYYYMMDD}-{n}. Sent to the EU registry as the batch identifier."
          },
          "expectedArrival": {
            "type": "string",
            "format": "date"
          },
          "note": {
            "type": "string",
            "maxLength": 500
          },
          "billedTo": {
            "type": "string",
            "enum": [
              "manufacturer"
            ],
            "description": "The manufacturer pays for this batch (optional; default: the link's choice). Checked against its plan: 403 with details.code = plan_limit when it has no room."
          }
        }
      },
      "AssignBatchResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "assigned",
              "confirmed",
              "rejected",
              "withdrawn"
            ]
          },
          "items": {
            "type": "integer"
          },
          "billedTo": {
            "type": "string",
            "enum": [
              "importer",
              "manufacturer"
            ],
            "description": "Who pays for the passports of the batch: they count in the payer's plan when the importer confirms it."
          },
          "skipped": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Serial numbers not assigned (unknown or already in a batch)."
          }
        }
      },
      "ImportBatch": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "assigned",
              "confirmed",
              "rejected",
              "withdrawn"
            ]
          },
          "items": {
            "type": "integer"
          },
          "billedTo": {
            "type": "string",
            "enum": [
              "importer",
              "manufacturer"
            ],
            "description": "Who pays for the passports of the batch: they count in the payer's plan when the importer confirms it."
          },
          "modelId": {
            "type": "string"
          },
          "expectedArrival": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "assignedAt": {
            "type": "string",
            "format": "date-time"
          },
          "decidedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "serials": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "serial": {
                  "type": "string"
                },
                "uri": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
