{"openapi":"3.1.0","info":{"title":"DPP Host API","version":"1.0.0","summary":"Create, validate and publish Digital Product Passports, and manage Premium brand pages.","description":"The operations of DPP Host REST API v1 that are built (docs/api/v1.md). The organisation always comes from the API key, never from a parameter.","contact":{"name":"DPP Host (Qubites ApS)","email":"hello@dpphost.eu"}},"servers":[{"url":"https://api.dpphost.eu/v1"}],"security":[{"apiKey":[]}],"tags":[{"name":"account"},{"name":"rule-sets"},{"name":"products"},{"name":"lots"},{"name":"uploads"},{"name":"releases"},{"name":"drafts"},{"name":"passports"},{"name":"approvals"},{"name":"imports"},{"name":"media"},{"name":"pages"},{"name":"webhooks"},{"name":"audit"},{"name":"owners"},{"name":"handovers"},{"name":"bikes"},{"name":"factory-files"},{"name":"stock"},{"name":"deliveries"},{"name":"connections"}],"paths":{"/openapi.json":{"get":{"tags":["account"],"operationId":"getOpenApi","summary":"This document (the built operations only); no API key needed","x-dpp-pr":7,"x-dpp-plan":"free","security":[],"responses":{"200":{"description":"OpenAPI 3.1","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/me":{"get":{"tags":["account"],"operationId":"getMe","summary":"The key's organisation, scopes, limits, verification status and prepaid balance","x-dpp-pr":7,"x-dpp-plan":"free","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/rule-sets":{"get":{"tags":["rule-sets"],"operationId":"listRuleSets","summary":"The rule sets the organisation may pick","description":"Sets whose availability is on, and pilot sets of which the organisation is a pilot (docs/api/v1.md §6b). A set that is off is never listed.\n","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RuleSet"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/rule-sets/{rule_set_id}":{"get":{"tags":["rule-sets"],"operationId":"getRuleSet","summary":"A rule set with its fields","description":"Each field with its audiences, data level, applicability, legal references, unit, format and term lists (docs/api/v1.md §6b). 404 not_found for a set the organisation may not pick, unless one of its products uses it.\n","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"rule_set_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSetDetail"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/rule-sets/{rule_set_id}/terms":{"get":{"tags":["rule-sets"],"operationId":"getRuleSetTerms","summary":"The term lists of a rule set's fields, with labels in one language","description":"Each list the set's fields take codes from, whole and in its order, and access_role when a field names roles. Labels in the language asked for: the database's (the first 8 languages), else the passport page's (all 25). A list without a label for every code in that language comes in English, and its lang says so. 404 not_found for a set the organisation may not pick, unless one of its products uses it.\n","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"rule_set_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"lang","in":"query","description":"A page language; en when left out (400 bad_request for any other value).","schema":{"$ref":"#/components/schemas/ContentLocale"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleSetTerms"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products":{"get":{"tags":["products"],"operationId":"listProducts","summary":"List or search products","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"q","in":"query","schema":{"type":"string","maxLength":200},"description":"Search in title and SKU"},{"name":"gtin","in":"query","schema":{"$ref":"#/components/schemas/Gtin"}},{"name":"external_id","in":"query","schema":{"type":"string","maxLength":200}},{"name":"dpphost_id","in":"query","schema":{"type":"string","maxLength":40},"description":"A DPP Host ID of one of the product's sizes. Spaces and hyphens are removed and letters upper-cased first; 400 bad_request when it is not a valid ID."},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["products"],"operationId":"createProduct","summary":"Create a product","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCreate"}}}},"responses":{"201":{"description":"Created","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"get":{"tags":["products"],"operationId":"getProduct","summary":"Read a product","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"patch":{"tags":["products"],"operationId":"updateProduct","summary":"Change a product (overwrites the fields given)","description":"A key without passports:publish that changes the title or category marks the product record unconfirmed (record_confirmed false) until a person confirms it.\n","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductUpdate"}}}},"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["products"],"operationId":"deleteProduct","summary":"Delete a product without passports (its variants and draft values go with it)","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"204":{"description":"Deleted"},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/variants":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"get":{"tags":["products"],"operationId":"listVariants","summary":"List a product's variants","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Variant"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["products"],"operationId":"createVariant","summary":"Create a variant","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariantWrite"}}}},"responses":{"201":{"description":"Created","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Variant"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/variants/{variant_id}":{"parameters":[{"$ref":"#/components/parameters/VariantId"}],"get":{"tags":["products"],"operationId":"getVariant","summary":"Read a variant","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Variant"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"patch":{"tags":["products"],"operationId":"updateVariant","summary":"Change a variant","description":"The GTIN of a variant with passports is fixed.","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IfMatch"},{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariantWrite"}}}},"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Variant"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["products"],"operationId":"deleteVariant","summary":"Delete a variant without passports","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"204":{"description":"Deleted"},"default":{"$ref":"#/components/responses/Problem"}}}},"/variants/{variant_id}/lots":{"parameters":[{"$ref":"#/components/parameters/VariantId"}],"get":{"tags":["lots"],"operationId":"listLots","summary":"A size's lots, newest first","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lot"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["lots"],"operationId":"createLot","summary":"Create a lot of the size (a production batch, by its GS1 batch/lot code)","description":"409 conflict for a code the size has already. Made by a key without passports:publish, the lot is a record that waits for a person (record_confirmed false).\n","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotCreate"}}}},"responses":{"201":{"description":"Created; Location is the lot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lot"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}":{"parameters":[{"$ref":"#/components/parameters/LotId"}],"get":{"tags":["lots"],"operationId":"getLot","summary":"Read a lot","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lot"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["lots"],"operationId":"deleteLot","summary":"Delete a lot no passport belongs to (its values go with it)","description":"409 conflict while a passport belongs to the lot, or a serial upload or segment is still there.","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"204":{"description":"Deleted"},"default":{"$ref":"#/components/responses/Problem"}}},"patch":{"tags":["lots"],"operationId":"updateLot","summary":"Set the URL form of the lot's items (manifest lots, before the first release)","description":"with_lot (/01/{gtin}/10/{lot}/21/{serial}, the form for new codes) or without_lot (/01/{gtin}/21/{serial}, codes printed without the lot). Fixed at the lot's first release (docs/product/sparse-items.md §1.3, §6).\n","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lot"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/uploads":{"parameters":[{"$ref":"#/components/parameters/LotId"}],"get":{"tags":["uploads"],"operationId":"listLotUploads","summary":"A lot's serial uploads, newest first","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LotUpload"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["uploads"],"operationId":"createLotUpload","summary":"Open an upload of serial numbers for a manifest lot","description":"The lot's rule set must allow manifest lots (422 use_passports otherwise; batteries/draft-1 does) and the size needs a GTIN (422 rule_set_requires_gtin). The size's first upload makes it a manifest size: from then on it takes no item passports (POST /passports and /passports/bulk answer 422 use_manifest). The defaults are what every serial of the upload has unless its own entry says: the placing on the market (today or up to a year ago, 422 placed_on_market_invalid) and public item values a manifest may hold (422 field_not_in_manifest otherwise; for batteries the date of manufacture). Limits: 422 lot_segments_limit, too_many_open_uploads (docs/product/sparse-items.md §5.2, §5.5).\n","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotUploadCreate"}}}},"responses":{"201":{"description":"Opened; Location is the upload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotUpload"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/uploads/{upload_id}":{"parameters":[{"$ref":"#/components/parameters/UploadId"}],"get":{"tags":["uploads"],"operationId":"getLotUpload","summary":"Read an upload (status, counts, problems, its segment once built)","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotUpload"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["uploads"],"operationId":"deleteLotUpload","summary":"Discard an upload that is open, failed or ready","description":"Its staged serials and its segment record go with it. 409 upload_building while it is queued, building or waiting for its second copy.\n","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"204":{"description":"Discarded"},"default":{"$ref":"#/components/responses/Problem"}}}},"/uploads/{upload_id}/serials":{"parameters":[{"$ref":"#/components/parameters/UploadId"}],"post":{"tags":["uploads"],"operationId":"stageLotSerials","summary":"Add serials or ranges to an open upload (up to 100,000 items or 1,000 ranges, 8 MB)","description":"Every entry is checked before any is written, and the first problem refuses the request with the entry named in errors (item or range, from 1, and serial: the serial, or the range as prefix{from..to}). Serials are 1 to 20 characters of GS1 character set 82; a range is a prefix and a counter of one width from from to to (digit strings of equal length). A serial twice in the request, already in this upload, in another upload of the size, in a staged range or with an item passport of the size is 409 serial_taken; so is a range that overlaps another or covers a listed serial. 409 upload_not_open once the upload is closed. Limits: 422 upload_serials_limit, upload_ranges_limit, lot_serials_limit. Idempotent with an Idempotency-Key. The serials of the size's other segments are checked when the segment is built (problem serial_taken on the upload).\n","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotUploadStage"}}}},"responses":{"200":{"description":"Staged; the upload with its counts, and staged {items, ranges} of this request","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/LotUpload"},{"type":"object","properties":{"staged":{"type":"object","properties":{"items":{"type":"integer","minimum":0},"ranges":{"type":"integer","minimum":0}}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/uploads/{upload_id}/close":{"parameters":[{"$ref":"#/components/parameters/UploadId"}],"post":{"tags":["uploads"],"operationId":"closeLotUpload","summary":"Close an upload and queue its build","description":"The publisher Worker builds one segment from the upload (status building, then built), the backup Worker copies it to the backup bucket (ready), or the build reports its problems (failed: serial_taken against the size's other segments, missing_item_value). Poll GET /uploads/{upload_id}. 422 upload_empty without serials; 409 upload_not_open when it is closed already.\n","x-dpp-pr":"S2","x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotUpload"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/releases":{"parameters":[{"$ref":"#/components/parameters/LotId"}],"get":{"tags":["releases"],"operationId":"listLotReleases","summary":"A manifest lot's releases, newest first (the draft included)","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LotRelease"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["releases"],"operationId":"createLotRelease","summary":"Draft the lot's next release from its ready uploads, with its gap check","description":"A release is the numbered, immutable state of a manifest lot (docs/product/sparse-items.md §2.5, §5.3). The draft lists the ready uploads named in uploads (409 upload_not_ready for one without its second copy, 409 segment_released for one a release lists already), or every ready upload not yet released, with the pending serial states, the lot's current values, the model passport's current version as its link and the defaults (checked as an upload's; the previous release's when left out; the default placing on the market is fixed once the lot has a release, 409 placed_on_market_fixed). One draft per lot (409 release_draft_exists); a draft that would change nothing is 422 nothing_to_release; a size that stores item passports one by one is 422 use_passports; more than 1,000 states is 422 states_limit. The answer carries the gap check.\n","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotReleaseCreate"}}}},"responses":{"201":{"description":"Drafted; Location is the release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotRelease"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/releases/{release}":{"parameters":[{"$ref":"#/components/parameters/LotId"},{"$ref":"#/components/parameters/Release"}],"get":{"tags":["releases"],"operationId":"getLotRelease","summary":"Read a release by number, with its object and the gap check stored with it","description":"data is the release object dpphost.lot-release/1, what the resolver serves at s/{lot_id}/{n}.json once published (a draft's as last built); it never holds a restricted value. gap_check is the check stored with the release (a draft's from its making or last request; a published release's from its publish); run POST …/validate for the check as of now.\n","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotRelease"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["releases"],"operationId":"deleteLotRelease","summary":"Discard the draft release","description":"Its states wait for the next release; its segments keep their numbers. A published release never goes (409 release_published).","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"204":{"description":"Discarded"},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/validate":{"parameters":[{"$ref":"#/components/parameters/LotId"}],"post":{"tags":["releases"],"operationId":"validateLot","summary":"The gap check per lot, as of now","description":"The checks of POST /passports/{id}/validate once per lot, not per serial, judged once per distinct placing date: for the lot's draft release, else for what a new release would hold now (every ready upload, the current defaults). Changes nothing. A required restricted item field is missing until a lot default or an exception for every listed serial exists (POST /lots/{lot_id}/item-values).\n","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"The result, whether the release would pass or not","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotValidationResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/releases/{release}/validate":{"parameters":[{"$ref":"#/components/parameters/LotId"},{"$ref":"#/components/parameters/Release"}],"post":{"tags":["releases"],"operationId":"validateLotRelease","summary":"The gap check of one release, as of now (a published one on its day)","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"The result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotValidationResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/releases/{release}/publish":{"parameters":[{"$ref":"#/components/parameters/LotId"},{"$ref":"#/components/parameters/Release"}],"post":{"tags":["releases"],"operationId":"publishLotRelease","summary":"Publish the draft release, or request a human approval","description":"As POST /passports/{passport_id}/publish, for a release. 201 with the published release when the key may publish unattended (an admin granted it passports:publish; the organisation must be verified, else 403 org_not_verified). A key with passports:write only gets 202 with an approval of kind lot_release: the person who created the key approves it at approval.approve_url (app.dpphost.eu/approve/lot-release/{id}) with a fresh sign-in; the release is pinned by snapshot_hash. Both rebuild the object with today's values and run the gap check first. Refusals carry the codes of section 8 (org_not_verified, gtin_not_claimed, validation_failed, quota_exceeded, 402 balance_too_low when the prepaid balance does not cover the new serials) and 422 model_unpublished, 409 release_published. No body is needed.\n","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Published; Location is the release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotReleasePublishResult"}}}},"202":{"description":"A person must approve at approval.approve_url; Location is the approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotReleasePublishResult"}}}},"402":{"$ref":"#/components/responses/BalanceTooLow"},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/serial-states":{"parameters":[{"$ref":"#/components/parameters/LotId"}],"post":{"tags":["releases"],"operationId":"setLotSerialStates","summary":"Record serial states of a released lot (gone, override), each with the serial's proof","description":"1 to 1,000 states, each with the serial's proof (the entry part of the item's ?format=proof answer), which is checked against the roots of the lot's released segments before anything is written (422 serial_proof_required, 422 serial_proof_invalid, 404 serial_not_listed, 409 segment_not_released). gone takes reason recycled or listed_in_error (moderation and court_order are staff decisions, 403 staff_only); override names an in_manifest field with a value that passes its check, never the placing on the market (422 override_not_allowed); withdrawn and listed stay in the dashboard (403 dashboard_only); materialised is the system's (403 system_only). A gone serial takes no further state (409 serial_gone); 409 lot_not_released before the lot's first release. Every entry is checked before any is written; the first problem names the entry in errors (state, from 1, and serial). The states wait for the lot's next release, which carries them.\n","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerialStates"}}}},"responses":{"200":{"description":"Recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SerialStatesResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/lots/{lot_id}/item-values":{"parameters":[{"$ref":"#/components/parameters/LotId"}],"post":{"tags":["releases"],"operationId":"setLotItemValues","summary":"Set restricted per-item values of a lot (a default, exceptions per serial)","description":"For one restricted item field of the lot's rule set (an item field whose audiences are not public; 422 field_not_restricted for a public one, 422 wrong_level for a lot or model field): the default every listed serial has unless its own value says, or its removal (clear_default), and 1 to 1,000 exceptions {serial, value, proof} (value null removes one), each with the serial's proof. Values pass the field's check. A passports:publish key only: the values are the brand's statements. They are never in a release, in R2 or in any answer of this API, and no route reads them back; the answer holds counts only (docs/product/sparse-items.md §1.7, §2.6).\n","x-dpp-pr":"S6","x-dpp-plan":"free","x-dpp-scope":"passports:publish","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotItemValues"}}}},"responses":{"200":{"description":"Set; counts only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LotItemValuesResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/draft":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"get":{"tags":["drafts"],"operationId":"getDraft","summary":"Draft field values for the product, one variant, one lot or one item passport","description":"With lot_id, the size's values and the lot's; with passport_id, the size's, the lot's and the item passport's own (docs/api/v1.md §6a). One of lot_id and passport_id at most (400 bad_request with both).\n","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"variant_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"lot_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"A lot of the product (its values)."},{"name":"passport_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"An item passport of the product (its values)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"patch":{"tags":["drafts"],"operationId":"setDraftFields","summary":"Set, clear, confirm and reject draft field values (one transaction)","description":"Applied in this order, all or nothing: clear (value null), set confirmed values, set suggestions, confirm, reject. Clearing, confirmed values, confirm and reject need passports:publish (a key an admin granted it stands for the brand); other keys write suggestions only. Values written through MCP are always suggestions. With lot_id or passport_id the changes are the lot's or the item passport's values; a field is set at its data level only (422 wrong_level otherwise, docs/api/v1.md §6a).\n","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftPatchResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports":{"get":{"tags":["passports"],"operationId":"listPassports","summary":"List passports","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"product_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/PassportStatus"}},{"name":"gtin","in":"query","schema":{"$ref":"#/components/schemas/Gtin"},"description":"Never matches a DPP Host ID passport."},{"name":"dpphost_id","in":"query","schema":{"type":"string","maxLength":40},"description":"The passports at this DPP Host ID (model, batch and item). Spaces and hyphens are removed and letters upper-cased first; 400 bad_request when it is not a valid ID."},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Passport"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["passports"],"operationId":"createPassport","summary":"Create a passport draft","description":"resolver_path is derived by the server from the variant's GTIN or DPP Host ID, batch and serial. A variant without either gets a DPP Host ID here when its product's rule set has requires_gtin false (422 validation_failed otherwise; 422 rule_set_requires_gtin for an ID size under a rule set that requires a GTIN). Before the organisation is verified it holds at most 25 drafts (403 draft_limit_reached). A level the rule set does not allow is 422 wrong_level; an item passport of a rule set with lot or item values needs a GTIN (422 rule_set_requires_gtin) and may name its lot and its placing on the market (422 placed_on_market_invalid, docs/api/v1.md §6a).\n","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassportCreate"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Passport"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports/bulk":{"post":{"tags":["passports"],"operationId":"createPassportsBulk","summary":"Create 1 to 10,000 item passports of one size, with their lots and item values","description":"One transaction: every item and value is checked before anything is written, and the first problem refuses the request with the item named in errors (item, from 1, and serial). A serial twice in the request, or a path that has a passport already, is 409 conflict. Nothing is approved or published. Before the organisation is verified the request counts as a whole against its 25 drafts (403 draft_limit_reached). The body may be up to 8 MB (docs/api/v1.md §6a).\n","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassportBulkCreate"}}}},"responses":{"201":{"description":"Created, in the items' order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassportBulkResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports/{passport_id}":{"parameters":[{"$ref":"#/components/parameters/PassportId"}],"get":{"tags":["passports"],"operationId":"getPassport","summary":"Read a passport","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Passport"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"patch":{"tags":["passports"],"operationId":"updatePassport","summary":"Set an item passport's placing on the market before its first publish","description":"null clears it (the item is then placed on the market on the day of its first publish). 422 placed_on_market_invalid for a day after today or more than a year ago; 409 placed_on_market_fixed once the passport is published. Set by a key without passports:publish, the passport's record waits for a person.\n","x-dpp-pr":"1g","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassportUpdate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Passport"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["passports"],"operationId":"deletePassportDraft","summary":"Delete a draft passport that was never published","description":"Published and withdrawn passports and their versions are never deleted.","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"204":{"description":"Deleted"},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports/{passport_id}/validate":{"parameters":[{"$ref":"#/components/parameters/PassportId"}],"post":{"tags":["passports"],"operationId":"validatePassport","summary":"Run the gap check without publishing","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"The result, whether valid or not","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports/{passport_id}/versions":{"parameters":[{"$ref":"#/components/parameters/PassportId"}],"get":{"tags":["passports"],"operationId":"listPassportVersions","summary":"Version history","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PassportVersionSummary"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports/{passport_id}/versions/{version}":{"parameters":[{"$ref":"#/components/parameters/PassportId"},{"name":"version","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"get":{"tags":["passports"],"operationId":"getPassportVersion","summary":"One version with its data and JSON-LD","x-dpp-pr":7,"x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PassportVersion"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/passports/{passport_id}/publish":{"parameters":[{"$ref":"#/components/parameters/PassportId"}],"post":{"tags":["passports"],"operationId":"publishPassport","summary":"Publish the draft, or request a human approval","description":"201 with the new version when the key may publish unattended (an admin granted it passports:publish; the organisation must be verified, else 403 org_not_verified). A key with passports:write only gets 202 with an approval: the person who created the key (still an editor or more) approves it at approval.approve_url with a fresh sign-in. Refusals carry the codes of section 8 (org_not_verified, gtin_not_claimed, validation_failed, version_too_large, quota_exceeded, draft_changed, resolver_path_taken ...), and 402 balance_too_low when the prepaid balance does not cover a first publish (needed, price, balance, currency, top_up_url).\n","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishRequest"}}}},"responses":{"201":{"description":"Published; Location is the new version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishResult"}}}},"202":{"description":"A person must approve at approval.approve_url; Location is the approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishResult"}}}},"402":{"$ref":"#/components/responses/BalanceTooLow"},"default":{"$ref":"#/components/responses/Problem"}}}},"/approvals/{approval_id}":{"get":{"tags":["approvals"],"operationId":"getApproval","summary":"Status of a publish or page-release approval","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"approval_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Approval"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/imports":{"get":{"tags":["imports"],"operationId":"listImports","summary":"List import jobs","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"imports:write","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ImportJob"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["imports"],"operationId":"startImport","summary":"Start an import job (drafts only, never publishes)","description":"A JSON body with rows, or a text/csv body (at most 8 MB) with the options in the query. The key needs passports:write too; confirm needs passports:publish. At most 5 jobs of an organisation wait or run at a time (429 rate_limited).\n","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"imports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"rule_set_id","in":"query","description":"CSV bodies: the rule set of new products.","schema":{"type":"string","format":"uuid"}},{"name":"confirm","in":"query","description":"CSV bodies: store values as confirmed (passports:publish).","schema":{"type":"boolean","default":false}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCreate"}},"text/csv":{"schema":{"type":"string","description":"A header row, then one row per line; see section 9 for the columns."}}}},"responses":{"202":{"description":"Accepted; Location is the job. Poll it or wait for import.finished","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJob"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/imports/{import_id}":{"get":{"tags":["imports"],"operationId":"getImport","summary":"Status of an import job and the errors of its failed rows","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"imports:write","parameters":[{"name":"import_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportJobDetail"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/imports/{import_id}/rows":{"get":{"tags":["imports"],"operationId":"listImportRows","summary":"The report of every row, in row order","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"imports:write","parameters":[{"name":"import_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","ok","failed"]}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ImportRow"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/media/uploads":{"post":{"tags":["media"],"operationId":"createMediaUpload","summary":"Get a one-time upload URL for one image","description":"Makes a media record (purpose) or takes an existing pending one (media_id), and answers a one-time URL on assets.dpphost.eu, valid 15 minutes, for one image of the declared type and at most the declared size. PUT the bytes there with that Content-Type. The asset host checks what the bytes are, removes EXIF, XMP and other metadata (a JPEG stored turned and an AVIF with metadata items are refused), and stores the file under its content address. On Free only the identification photo and the organisation's logo (made in the dashboard); with Premium the media library too; every image at most 300 KB; 2 GB per organisation. SVG is refused.\n","x-dpp-pr":14,"x-dpp-plan":"free","x-dpp-scope":"media:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUploadCreate"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUpload"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/media/{media_id}":{"get":{"tags":["media"],"operationId":"getMedia","summary":"Read a media asset","x-dpp-pr":14,"x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAsset"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/theme":{"get":{"tags":["pages"],"operationId":"getTheme","summary":"The organisation's brand theme","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Theme"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"put":{"tags":["pages"],"operationId":"setTheme","summary":"Replace the brand theme","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"pages:write","parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Theme"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Theme"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/pages":{"get":{"tags":["pages"],"operationId":"listPages","summary":"List brand pages","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"passports:read","parameters":[{"name":"product_id","in":"query","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BrandPage"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["pages"],"operationId":"createPage","summary":"Create a brand page for a product","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"pages:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"product_id for a product page (one per product), or kind brand for the organisation's brand page.","properties":{"product_id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["brand","product"]}}}}}},"responses":{"201":{"description":"Created","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandPage"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/pages/{page_id}":{"parameters":[{"$ref":"#/components/parameters/PageId"}],"get":{"tags":["pages"],"operationId":"getPage","summary":"Read a brand page with its draft blocks","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandPage"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/pages/{page_id}/blocks":{"parameters":[{"$ref":"#/components/parameters/PageId"}],"put":{"tags":["pages"],"operationId":"updatePageBlocks","summary":"Replace the page draft's blocks","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"pages:write","parameters":[{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["blocks"],"properties":{"blocks":{"type":"array","maxItems":40,"items":{"$ref":"#/components/schemas/PageBlock"}},"locales":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"The page's languages (default: unchanged)."},"default_locale":{"type":"string","description":"One of locales (default: unchanged)."}}}}}},"responses":{"200":{"description":"OK","headers":{"ETag":{"$ref":"#/components/headers/ETag"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandPage"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/pages/{page_id}/versions":{"parameters":[{"$ref":"#/components/parameters/PageId"}],"get":{"tags":["pages"],"operationId":"listPageVersions","summary":"Page version history","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"passports:read","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PageVersion"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/pages/{page_id}/releases":{"parameters":[{"$ref":"#/components/parameters/PageId"}],"post":{"tags":["pages"],"operationId":"releasePage","summary":"Release a page version; releasing an older version is a rollback","description":"Runs the release checks (bound numbers, Directive 2024/825 claims, alt text). 201 when the key may release unattended (an admin granted pages:release); 202 with an approval that a signed-in editor approves at approval.approve_url otherwise (pages:write is enough to ask). Content the checks block is 422 release_blocked with every reason and no approval.\n","x-dpp-pr":14,"x-dpp-plan":"premium","x-dpp-scope":"pages:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/IfMatch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"page_version":{"type":"integer","minimum":1,"description":"Version to release again (a rollback). Omit to release the current draft."},"draft_revision":{"type":"integer","minimum":1,"description":"The draft revision the client reviewed (or If-Match with the page's ETag); default the current draft."}}}}}},"responses":{"201":{"description":"Released","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseResult"}}}},"202":{"description":"A person must approve at approval.approve_url","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/webhooks":{"get":{"tags":["webhooks"],"operationId":"listWebhooks","summary":"List webhooks (at most 10 per organisation; one page)","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["webhooks"],"operationId":"createWebhook","summary":"Create a webhook; the signing secret is returned once","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookWrite"},{"required":["url","events"]}]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/webhooks/{webhook_id}":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"get":{"tags":["webhooks"],"operationId":"getWebhook","summary":"Read a webhook","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"patch":{"tags":["webhooks"],"operationId":"updateWebhook","summary":"Change a webhook's URL, events or enabled flag","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWrite"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["webhooks"],"operationId":"deleteWebhook","summary":"Delete a webhook","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","responses":{"204":{"description":"Deleted"},"default":{"$ref":"#/components/responses/Problem"}}}},"/webhooks/{webhook_id}/rotate-secret":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"post":{"tags":["webhooks"],"operationId":"rotateWebhookSecret","summary":"Issue a new signing secret; the old one keeps signing for overlap_hours (24 by default)","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"overlap_hours":{"type":"integer","minimum":0,"maximum":168,"default":24}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/webhooks/{webhook_id}/deliveries":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"get":{"tags":["webhooks"],"operationId":"listWebhookDeliveries","summary":"The delivery log, newest attempt first (kept 30 days)","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"webhooks:manage","parameters":[{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/audit-events":{"get":{"tags":["audit"],"operationId":"listAuditEvents","summary":"The organisation's audit log, newest first","x-dpp-pr":12,"x-dpp-plan":"free","x-dpp-scope":"audit:read","parameters":[{"name":"channel","in":"query","schema":{"$ref":"#/components/schemas/Channel"}},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Page"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditEvent"}}}}]}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/owner-items/claim-codes":{"post":{"tags":["owners"],"operationId":"issueClaimCodes","summary":"Claim codes for items (shown once)","x-dpp-pr":17,"x-dpp-plan":"premium","x-dpp-scope":"owners:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/ClaimCodeItem"}}}}}}},"responses":{"200":{"description":"One answer per item, in the same order. Codes and claim links appear only in this answer.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ClaimCodeResult"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/owner-items/release":{"post":{"tags":["owners"],"operationId":"releaseOwnerItem","summary":"End an item's registration and revoke its unused code","x-dpp-pr":17,"x-dpp-plan":"premium","x-dpp-scope":"owners:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerItemRef"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"released":{"type":"boolean"},"item":{"$ref":"#/components/schemas/OwnerItem"}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/owner-requests/{request_id}":{"get":{"tags":["owners"],"operationId":"getOwnerRequest","summary":"A repair request, guarantee claim or dispute from the owner area (with the attached receipt's type and size, never the file)","x-dpp-pr":17,"x-dpp-plan":"premium","x-dpp-scope":"owners:read","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerRequest"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers":{"get":{"tags":["handovers"],"operationId":"listHandovers","summary":"The shop's handovers, newest first (the last 30 days by default)","description":"Each handover as GET …/handovers/{handover_id} answers it. since: created at or after (default 30 days ago); order_ref: only that order reference; limit 1 to 200 (default 50). No cursor.\n","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"order_ref","in":"query","schema":{"type":"string","maxLength":64}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Handover"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["handovers"],"operationId":"createHandover","summary":"Hand 1 to 10 items of one brand over to a customer, at the counter (live) or by a one-time link (link)","description":"Every item must be a GTIN of a linked brand's product in the link's scope with a serial, not registered, not marked lost or stolen and in no open handover; a refusal is 422 validation_failed with every problem in errors ({index, gtin, serial, code, message}, gtin and serial when the item could be read: unavailable, not_linked, unknown, duplicate, other_brand). brand_org_id may be left out when the items name one linked brand. With email the link goes to the customer by email (kind link only) and the answer carries no link_url; otherwise a link handover answers link_url this once (never in an Idempotency-Key replay). 422 with code paused while the brand's owner services are paused; 429 rate_limited after 500 handovers a day per shop.\n","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverCreate"}}}},"responses":{"201":{"description":"Created; Location names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/eligibility":{"post":{"tags":["handovers"],"operationId":"checkHandoverEligibility","summary":"Whether a linked brand covers each GTIN's product and its owner services are live (for the shop plugins)","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["gtins"],"properties":{"gtins":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"One answer per GTIN, in the same order","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HandoverEligibility"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}":{"get":{"tags":["handovers"],"operationId":"getHandover","summary":"A handover's state, asked every few seconds while the counter shows it (keeps a live QR open)","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/approve":{"post":{"tags":["handovers"],"operationId":"approveHandover","summary":"The staff's yes: completes a live handover whose customer waits (every item or none); none waiting: the state as it is","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/decline":{"post":{"tags":["handovers"],"operationId":"declineHandover","summary":"The staff's no: ends the waiting customer's reservation; the QR shows again","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/cancel":{"post":{"tags":["handovers"],"operationId":"cancelHandover","summary":"Cancels an open handover; a finished one is answered as it is","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/send":{"post":{"tags":["handovers"],"operationId":"sendHandover","summary":"Email the customer a one-time link (an open handover of either kind becomes a link with a new code)","description":"The address is kept only in the queued email. 422 with code not_open for a finished handover; 429 rate_limited after 200 handover emails a day per shop.\n","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverEmail"}}}},"responses":{"200":{"description":"The state (a link, emailed now)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/link":{"post":{"tags":["handovers"],"operationId":"newHandoverLink","summary":"A new one-time link for an open handover (the old code stops); answered once","x-dpp-pr":"RH-A","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The link, this once (never in an Idempotency-Key replay)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverLink"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/return":{"post":{"tags":["handovers"],"operationId":"returnHandover","summary":"It came back: undo a done handover within 30 days for the items that came back (by default every item still registered from it)","description":"retailer_handover_return. Each item's registration from this handover ends (the owner's card says \"Returned to {shop} on {date}\"), the item is back in the shop's stock and the brand gets owner.returned. A key only, never a shop connection. 422 validation_failed with code not_done or too_late, or with the items' problems in errors (not_in_handover, not_registered, duplicate).\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverReturn"}}}},"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/handovers/{handover_id}/receipt":{"post":{"tags":["handovers"],"operationId":"beginHandoverReceipt","summary":"Add the customer's receipt to an open handover: the receipts Worker's address and the headers of the one PUT that sends the file","description":"retailer_receipt_begin. content_type is application/pdf, image/jpeg or image/png; bytes the file's size, 1 to 5,242,880. The answer names the one PUT that sends the file to the receipts Worker (receipts.dpphost.eu), from the shop's server: upload_url with exactly headers (Authorization carries the upload's one-time token, answered this once and never in an Idempotency-Key replay), a Content-Length and the bytes as the body, once, before expires_at (15 minutes). The PUT answers 201 {receipt_id, bytes, mime_type, removed_metadata}; then the handover's receipt shows it. A receipt already on the handover is replaced. 409 receipts_off (the brand does not take receipts yet), customer_waiting (a customer waits for the staff's yes: decline, add the receipt, and the customer scans again) or not_open; 403 terms_outdated with terms_version (accept the current Retailer Terms first); 422 validation_failed with code paused (the brand's owner services), content_type or bytes; 429 rate_limited after 500 receipts a day per shop.\n","x-dpp-pr":"BM-C","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"},{"$ref":"#/components/parameters/HandoverId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptUploadCreate"}}}},"responses":{"201":{"description":"The upload, its headers this once (an Idempotency-Key replay answers without them)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptUpload"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["handovers"],"operationId":"removeHandoverReceipt","summary":"Take the receipt off an open handover (its file is deleted); none there changes nothing","description":"retailer_receipt_remove. 409 not_open once the handover ended (a receipt the customer kept is the owner's, and any other went with the end).\n","x-dpp-pr":"BM-C","x-dpp-plan":"free","x-dpp-scope":"handovers:write","parameters":[{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/bikes":{"post":{"tags":["bikes"],"operationId":"createBike","summary":"Make a bike under the bike rules (bicycles/1) with its set-up, a bicycle or an e-bike and its battery","description":"create_bike. 422 rule_set_unavailable while the bike rules are not available to the organisation. An e-bike's battery is the brand's own battery product ({product: {id}}, or {product: {title}} for a new one under batteries/draft-1 with the category LMT, 422 battery_rule_set_unavailable while that set is not available to the organisation) or a motor system maker's battery model ({maker, model, gtin?}). A key without passports:publish writes the kind and the battery models as suggestions a person confirms. Sizes come after (POST /products/{product_id}/variants, each with its GTIN). The set-up's content is the database's check: 422 validation_failed with errors[].code kind, battery or battery_product.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeCreate"}}}},"responses":{"201":{"description":"Made; Location is the bike's set-up (GET /products/{product_id}/bike)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeCreated"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/bike":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"get":{"tags":["bikes"],"operationId":"getBike","summary":"A bike's set-up; 404 for a product that has none (it is not a bike)","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeSetup"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"put":{"tags":["bikes"],"operationId":"setBikeSetup","summary":"Replace a bike's set-up, whole","description":"set_bike_setup: the kind and, for an e-bike, the battery as POST /bikes takes them. The kind and the battery models follow as values (suggestions for a key without passports:publish). 422 validation_failed with errors[].code rule_set for a product that is not a bike.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeSetupUpdate"}}}},"responses":{"200":{"description":"The set-up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeSetup"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/batches":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"get":{"tags":["bikes"],"operationId":"listBikeBatches","summary":"A bike's batches, newest first, with their sizes, own batteries, counts, status and factory files","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BikeBatch"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/batches/{batch}/copy":{"parameters":[{"$ref":"#/components/parameters/ProductId"},{"$ref":"#/components/parameters/Batch"}],"post":{"tags":["bikes"],"operationId":"copyBikeBatch","summary":"Copy as next batch, the batch values as suggestions to confirm again","description":"copy_batch: the lot to for every size, and every own battery size, that has the batch, each with the confirmed values of its lot as suggestions. 409 batch_exists for a batch code the bike has; 404 for an unknown batch.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeBatchCopy"}}}},"responses":{"201":{"description":"The next batch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeBatchCopied"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/batches/{batch}/labels":{"parameters":[{"$ref":"#/components/parameters/ProductId"},{"$ref":"#/components/parameters/Batch"}],"get":{"tags":["bikes"],"operationId":"listBikeLabelRows","summary":"The rows of a batch's labels, in frame number order, by page; never a claim code","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"after","in":"query","description":"The next of the previous page (a frame number)","schema":{"type":"string","maxLength":20}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BikeLabelRow"}},"next":{"type":["string","null"],"description":"The last frame number when more follow: the next page's after"}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/products/{product_id}/copy":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"post":{"tags":["bikes"],"operationId":"copyBikeModel","summary":"A new model year of a bike, its values as suggestions and its sizes without GTINs","description":"copy_product: the bike under the same rules with its category and kind, its sizes' titles (no GTIN or SKU), every other model value as a suggestion, its set-up and its guarantee. Adding the copy to the shop links that list the bike is a signed-in admin's change in the dashboard; for a key shops is null whatever with_shops says.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeCopy"}}}},"responses":{"201":{"description":"Made; Location is the copy's set-up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BikeCopied"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/factory-files/check":{"post":{"tags":["factory-files"],"operationId":"checkFactoryFile","summary":"Check a factory file's rows before anything is saved; writes nothing","description":"factory_file_check: 1 to 5,000 rows in a body of up to 2 MB. Every problem by row (a frame number twice is found within one call), the warnings and the counts. 422 bike_setup for a bike without its set-up; 422 validation_failed with errors[].code rule_set for a product that is not a bike, batch or battery_batch for a code that is not one, rows for a row number given twice.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryFileCheck"}}}},"responses":{"200":{"description":"The check","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryCheckResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/factory-files":{"get":{"tags":["factory-files"],"operationId":"listFactoryFiles","summary":"The organisation's factory files, newest first (at most 500), of one bike and batch when given","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:read","parameters":[{"name":"product_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"batch","in":"query","schema":{"type":"string","maxLength":20}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FactoryFile"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["factory-files"],"operationId":"openFactoryFile","summary":"Open the factory file of a bike's batch (one open file per batch)","description":"factory_file_open. 409 file_open with the open file's id as file_id when the batch has one; 422 too_many_open_files after 20 open files in the organisation; 422 bike_setup for a bike without its set-up.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryFileCreate"}}}},"responses":{"201":{"description":"Opened; Location names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryFile"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/factory-files/{file_id}":{"parameters":[{"$ref":"#/components/parameters/FileId"}],"get":{"tags":["factory-files"],"operationId":"getFactoryFile","summary":"A factory file with its counts, first problems and serial lists","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryFile"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["factory-files"],"operationId":"discardFactoryFile","summary":"Discard an open factory file with its stock items and serial lists; answers the file","description":"factory_file_discard. 409 file_used once a bike or battery of the file is no longer in the brand's stock (remove the others with POST /stock/remove); 409 file_not_open for a closed or discarded file.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"200":{"description":"Discarded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryFile"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/factory-files/{file_id}/rows":{"parameters":[{"$ref":"#/components/parameters/FileId"}],"post":{"tags":["factory-files"],"operationId":"addFactoryFileRows","summary":"Add a factory file's rows; those without a problem list their bikes, and the frames added before count as already","description":"factory_file_rows: the check, then each row without a problem: the frame number listed in its size's lot of the batch (the lot and the file's serial list of it made when missing) and each of the brand's own batteries in its size's lot of the battery batch, a stock item held by the brand for each, the factory's pairing. 1 to 5,000 rows in a body of up to 2 MB; an Idempotency-Key replays the first answer. 409 file_not_open for a closed file; 422 file_full after 100,000 rows in one file; 422 stock_limit at the organisation's limit of stock items.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryRows"}}}},"responses":{"200":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryRowsResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/factory-files/{file_id}/close":{"parameters":[{"$ref":"#/components/parameters/FileId"}],"post":{"tags":["factory-files"],"operationId":"closeFactoryFile","summary":"Close a factory file and queue its serial lists' builds; nothing is published","description":"factory_file_close. 422 file_empty for a file that added no bike (discard it instead); 409 file_not_open for a closed or discarded file. Each size's lot of the batch is then released as any manifest lot (section 6d).\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"passports:write","responses":{"200":{"description":"Closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactoryFile"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/stock":{"get":{"tags":["stock"],"operationId":"listStock","summary":"The stock the organisation may see, newest first; a brand's products' stock with who holds it","description":"stock_list. For a brand every bike and own battery of its products, for a business what it holds of other brands'. q is a frame or battery serial number, whole or its first 3 characters or more (a bike is found by the serial number of a battery fitted to it too). Never an owner, a code or a receipt.\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"stock:read","parameters":[{"name":"brand","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"product","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"variant","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"batch","in":"query","schema":{"type":"string","maxLength":20}},{"name":"state","in":"query","schema":{"type":"string","enum":["in_stock","missing","sold","taken_back","removed"]}},{"name":"q","in":"query","schema":{"type":"string","maxLength":20}},{"name":"holder","in":"query","description":"A brand: the items one business holds (BM-B)","schema":{"type":"string","format":"uuid"}},{"name":"notice","in":"query","description":"The items a published safety notice of the brand applies to (BM-B)","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"after","in":"query","description":"The next of the previous page","schema":{"type":"string","maxLength":100}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StockItem"}},"next":{"type":["string","null"],"description":"The next page's after; null on the last page"}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/stock/summary":{"get":{"tags":["stock"],"operationId":"getStockSummary","summary":"The stock's counts, per holder for a brand and per brand for a business that holds other brands' stock","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"stock:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockSummary"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/stock/remove":{"post":{"tags":["stock"],"operationId":"removeStock","summary":"The brand removes bikes or batteries it added by mistake, each removed or refused with its problem","description":"stock_remove: 1 to 1,000 items, each held by the brand in stock, never delivered and not registered. The batch's serial list still names a removed item (listed): once the batch is released, mark it gone with the reason listed_in_error (POST /lots/{lot_id}/serial-states).\n","x-dpp-pr":"BM-A","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockRemove"}}}},"responses":{"200":{"description":"Each item removed or refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockRemoveResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/stock/missing":{"post":{"tags":["stock"],"operationId":"markStockMissing","summary":"The business that holds items marks them lost or stolen; nobody can register them until they are found","description":"stock_mark_missing: 1 to 1,000 items the organisation holds in stock; with with_parts (default true) a bike's paired batteries it holds too. Each is marked or refused with its problem. An item's open handover ends and the brand gets stock.missing. Never an MCP client.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockMarkMissing"}}}},"responses":{"200":{"description":"Each item marked or refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockMarkResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/stock/found":{"post":{"tags":["stock"],"operationId":"markStockFound","summary":"Items marked lost or stolen are found again, by the business that marked them or by the brand","description":"stock_mark_found: each item is in stock again with the business that holds it, and its owner record's freeze from the mark is lifted. Never an MCP client.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockItemsInput"}}}},"responses":{"200":{"description":"Each item found or refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockMarkResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/delivery-receivers":{"get":{"tags":["deliveries"],"operationId":"listDeliveryReceivers","summary":"Who the organisation may send a brand's products to, the brand first","description":"delivery_receivers. The brand sends to every business it linked; a distributor to the brand and the shops it added; a shop to the brand, the distributor that added it and, while the brand allows shop-to-shop moves, another shop of the brand found by 3 characters or more of its name (q; at most 10). Never an MCP client.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"name":"brand","in":"query","description":"The brand whose products go; the key's own organisation by default","schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","schema":{"type":"string","maxLength":100}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryReceiver"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/deliveries":{"get":{"tags":["deliveries"],"operationId":"listDeliveries","summary":"The deliveries the organisation made or received, and for a brand every sent delivery of its products","description":"deliveries_list, newest first (sent, else made). direction out: what the organisation made, drafts included; in: what was sent to it. Without serials (GET /deliveries/{delivery_id}). No cursor.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:read","parameters":[{"name":"direction","in":"query","schema":{"type":"string","enum":["in","out"]}},{"name":"brand","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Delivery"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["deliveries"],"operationId":"createDelivery","summary":"A delivery from this organisation, its items and, with send, sent, in one call","description":"delivery_create, delivery_add and, with send, delivery_send, in one transaction. Without send the answer is the draft with problems, the items that could not be added. With send it is all or nothing: an item that cannot go is 422 validation_failed with every item in errors. 409 holder_only_starts for the brand's first sent delivery without start_holder_only; 403 terms_outdated for a business that has not accepted the current Retailer Terms; 429 rate_limited after 500 deliveries (or 1,000 drafts) a day.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryCreate"}}}},"responses":{"201":{"description":"Created; Location names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/deliveries/{delivery_id}":{"parameters":[{"$ref":"#/components/parameters/DeliveryId"}],"get":{"tags":["deliveries"],"operationId":"getDelivery","summary":"A delivery with its serials, for its sender, its receiver and the brand; a sender's draft with what stops it","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:read","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/deliveries/{delivery_id}/items":{"parameters":[{"$ref":"#/components/parameters/DeliveryId"}],"post":{"tags":["deliveries"],"operationId":"addDeliveryItems","summary":"Add 1 to 1,000 items to the sender's draft; each that cannot go is a problem, the others are added","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StockItemsInput"}}}},"responses":{"200":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryAddResult"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["deliveries"],"operationId":"removeDeliveryItems","summary":"Take items out of the sender's draft by their stock item ids; a bike's batteries go with it","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryRemove"}}}},"responses":{"200":{"description":"The draft","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/deliveries/{delivery_id}/send":{"parameters":[{"$ref":"#/components/parameters/DeliveryId"}],"post":{"tags":["deliveries"],"operationId":"sendDelivery","summary":"Send the sender's draft, all or nothing; the items are the receiver's stock from then on","description":"delivery_send. 422 validation_failed with every item that cannot go in errors, or with code empty, receiver or not_draft; 409 holder_only_starts; 403 terms_outdated; 429 rate_limited after 500 deliveries a day.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliverySend"}}}},"responses":{"200":{"description":"Sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/deliveries/{delivery_id}/cancel":{"parameters":[{"$ref":"#/components/parameters/DeliveryId"}],"post":{"tags":["deliveries"],"operationId":"cancelDelivery","summary":"Give the sender's draft up; its items are free again","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/deliveries/{delivery_id}/undo":{"parameters":[{"$ref":"#/components/parameters/DeliveryId"}],"post":{"tags":["deliveries"],"operationId":"undoDelivery","summary":"Take a delivery sent by mistake back within 14 days, while none of its items moved on","description":"delivery_undo. 422 validation_failed with each moved item in errors, or with code not_sent or too_late. The receiver's editors get the email delivery_undone.\n","x-dpp-pr":"BM-B","x-dpp-plan":"free","x-dpp-scope":"stock:write","responses":{"200":{"description":"Undone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Delivery"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connect/requests":{"post":{"tags":["connections"],"operationId":"createConnectRequest","summary":"Register a request to link a shop; the admin approves it on the consent page (§17)","description":"The connector's server calls this and keeps the PKCE verifier. Valid 30 minutes, used once. WooCommerce only here; Shopify shops link through the internal entrypoint of the DPP Host app in Shopify (403 forbidden otherwise). redirect_uri must be https (http only on localhost or 127.0.0.1), on exactly shop_host, with no user info, fragment, backslash or white space.\n","x-dpp-pr":"SC-4","x-dpp-plan":"free","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectRequestCreate"}}}},"responses":{"201":{"description":"Registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectRequest"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connect/token":{"post":{"tags":["connections"],"operationId":"redeemConnectCode","summary":"Trade the handoff code, the PKCE verifier and the same redirect_uri for the connection credential (once)","description":"The code comes back to redirect_uri as ?code=…&state=… and is valid 60 seconds. At most 5 attempts per request; a wrong code, verifier or redirect_uri is 400 invalid_code and counts.\n","x-dpp-pr":"SC-4","x-dpp-plan":"free","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectTokenRequest"}}}},"responses":{"200":{"description":"The credential, shown once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionCredential"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection":{"get":{"tags":["connections"],"operationId":"getConnection","summary":"The shop's own connection","x-dpp-pr":"SC-4","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connection"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["connections"],"operationId":"deleteConnection","summary":"End the link (the plugin's Disconnect, or Shopify's app/uninstalled); nothing in DPP Host is deleted","description":"Works with an expired credential too, not with one flagged exposed. reason=uninstalled is for Shopify's app/uninstalled, internal entrypoint only (403 forbidden otherwise). purge=true (Shopify's shop/redact, internal entrypoint only) answers 501 not_implemented until connection items can be deleted.\n","x-dpp-pr":"SC-4","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"name":"reason","in":"query","schema":{"type":"string","enum":["revoked","uninstalled"],"default":"revoked"}},{"name":"purge","in":"query","schema":{"type":"string","enum":["true","false"],"default":"false"}}],"responses":{"200":{"description":"Ended","content":{"application/json":{"schema":{"type":"object","properties":{"connection_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["revoked","uninstalled"]}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/rotate":{"post":{"tags":["connections"],"operationId":"rotateConnection","summary":"A new credential, valid 12 months, shown once; rotate 30 days before credential_expires_at","description":"WooCommerce keeps the old credential working for 7 days (previous_valid_until), never past 24 months after the last admin approval (then 401 reconnect_required). Shopify: no overlap. Refused from a superseded or exposed credential.\n","x-dpp-pr":"SC-4","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"}],"responses":{"200":{"description":"The new credential, shown once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotatedConnectionCredential"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/sync":{"post":{"tags":["connections"],"operationId":"syncConnection","summary":"Queue up to 1,000 of the shop's products or sizes as a sync job (§17)","description":"An Idempotency-Key is required and is unique per connection: the same key with the same queued rows answers the same job (replay true, Idempotent-Replayed true), with other rows 422 idempotency_key_reused. Rows the server would refuse are not queued but listed in rejected by their position; when none is left the answer is 422 validation_failed with the same list. More than 1,000 rows or 2 MB is 413 payload_too_large. At most 5 jobs of a connection wait or run at once (429 rate_limited with Retry-After). The job runs within minutes; its rows are suggestions only and never confirm or publish anything.\n","x-dpp-pr":"SC-7","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"$ref":"#/components/parameters/ClientVersion"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionSyncRequest"}}}},"responses":{"202":{"description":"Queued, or the job of the same Idempotency-Key (replay true)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionSyncJob"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/items":{"get":{"tags":["connections"],"operationId":"listConnectionItems","summary":"The status feed of the shop's own items; poll it with the last next_cursor (§17)","description":"The connection's items whose state moved after the cursor, oldest move first. Items of a database transaction that is still running wait for the next poll, so a cursor never passes an item. The cursor never expires; keep next_cursor and send it as since next time. Without since the feed starts at the first item. Show the passport link only when link is set (state online or live_elsewhere).\n","x-dpp-pr":"SC-8","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"name":"since","in":"query","description":"next_cursor of the previous answer","schema":{"type":"string","pattern":"^[0-9]{1,18}[.][0-9a-f-]{36}$"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":500}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionItemPage"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers":{"get":{"tags":["connections"],"operationId":"listHandoversByConnection","summary":"The shop's handovers, newest first (the last 30 days by default)","description":"Each handover as GET …/handovers/{handover_id} answers it. since: created at or after (default 30 days ago); order_ref: only that order reference; limit 1 to 200 (default 50). No cursor. A connection sees and changes only the handovers it made.\n","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"order_ref","in":"query","schema":{"type":"string","maxLength":64}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Handover"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}},"post":{"tags":["connections"],"operationId":"createHandoverByConnection","summary":"Hand 1 to 10 items of one brand over to a customer, at the counter (live) or by a one-time link (link)","description":"Every item must be a GTIN of a linked brand's product in the link's scope with a serial, not registered, not marked lost or stolen and in no open handover; a refusal is 422 validation_failed with every problem in errors ({index, gtin, serial, code, message}, gtin and serial when the item could be read: unavailable, not_linked, unknown, duplicate, other_brand). brand_org_id may be left out when the items name one linked brand. With email the link goes to the customer by email (kind link only) and the answer carries no link_url; otherwise a link handover answers link_url this once (never in an Idempotency-Key replay). 422 with code paused while the brand's owner services are paused; 429 rate_limited after 500 handovers a day per shop. A connection sees and changes only the handovers it made. An Idempotency-Key is kept with the handover for 24 hours, per connection: the same key with the same request answers that handover as it is now (201, Idempotent-Replayed true, without link_url: ask POST …/link), with another request 422 idempotency_key_reused.\n","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/IdempotencyKeyOptional"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverCreate"}}}},"responses":{"201":{"description":"Created; Location names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/eligibility":{"post":{"tags":["connections"],"operationId":"checkHandoverEligibilityByConnection","summary":"Whether a linked brand covers each GTIN's product and its owner services are live (for the shop plugins)","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["gtins"],"properties":{"gtins":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"One answer per GTIN, in the same order","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HandoverEligibility"}}}}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}":{"get":{"tags":["connections"],"operationId":"getHandoverByConnection","summary":"A handover's state, asked every few seconds while the counter shows it (keeps a live QR open)","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}/approve":{"post":{"tags":["connections"],"operationId":"approveHandoverByConnection","summary":"The staff's yes: completes a live handover whose customer waits (every item or none); none waiting: the state as it is","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}/decline":{"post":{"tags":["connections"],"operationId":"declineHandoverByConnection","summary":"The staff's no: ends the waiting customer's reservation; the QR shows again","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}/cancel":{"post":{"tags":["connections"],"operationId":"cancelHandoverByConnection","summary":"Cancels an open handover; a finished one is answered as it is","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}/send":{"post":{"tags":["connections"],"operationId":"sendHandoverByConnection","summary":"Email the customer a one-time link (an open handover of either kind becomes a link with a new code)","description":"The address is kept only in the queued email. 422 with code not_open for a finished handover; 429 rate_limited after 200 handover emails a day per shop.\n","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverEmail"}}}},"responses":{"200":{"description":"The state (a link, emailed now)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}/link":{"post":{"tags":["connections"],"operationId":"newHandoverLinkByConnection","summary":"A new one-time link for an open handover (the old code stops); answered once","x-dpp-pr":"RH-A","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The link, this once (never in an Idempotency-Key replay)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoverLink"}}}},"default":{"$ref":"#/components/responses/Problem"}}}},"/connection/handovers/{handover_id}/receipt":{"post":{"tags":["connections"],"operationId":"beginHandoverReceiptByConnection","summary":"Add the customer's receipt to the connection's own open handover: the receipts Worker's address and the headers of the one PUT that sends the file","description":"As POST /handovers/{handover_id}/receipt, with the same body, answer and errors, for the handovers the connection made (another connection's: 404). The WooCommerce plugin sends the order's invoice and the Shopify app a proof of purchase drawn from the order, each only when the merchant turned it on. The answer and the PUT stay on the shop's server: the receipts Worker refuses a request with an Origin other than https://app.dpphost.eu. No Idempotency-Key here: each call begins a new upload, which replaces the receipt.\n","x-dpp-pr":"BM-C","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptUploadCreate"}}}},"responses":{"201":{"description":"The upload, its headers this once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptUpload"}}}},"default":{"$ref":"#/components/responses/Problem"}}},"delete":{"tags":["connections"],"operationId":"removeHandoverReceiptByConnection","summary":"Take the receipt off the connection's own open handover (its file is deleted); none there changes nothing","description":"retailer_receipt_remove. 409 not_open once the handover ended; another connection's handover: 404.\n","x-dpp-pr":"BM-C","x-dpp-plan":"free","security":[{"connectionCredential":[]}],"parameters":[{"$ref":"#/components/parameters/ClientVersion"},{"$ref":"#/components/parameters/HandoverId"}],"responses":{"200":{"description":"The state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Handover"}}}},"default":{"$ref":"#/components/responses/Problem"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"dpp_live_<prefix>_<secret>","description":"Created in the dashboard, shown once, stored as an HMAC. Scopes are capped by the creator's role. Never send a key in a URL.\n"},"connectionCredential":{"type":"http","scheme":"bearer","bearerFormat":"dpp_conn_<prefix>_<secret>","description":"A shop connection's credential (§17), from POST /connect/token or \"add manually\". It reaches the /connection routes only (403 forbidden elsewhere). Never send it in a URL.\n"}},"parameters":{"Limit":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},"Cursor":{"name":"cursor","in":"query","schema":{"type":"string","maxLength":512}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","minLength":1,"maxLength":255}},"IdempotencyKeyOptional":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},"IfMatch":{"name":"If-Match","in":"header","required":false,"schema":{"type":"string"}},"ProductId":{"name":"product_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"VariantId":{"name":"variant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"PassportId":{"name":"passport_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"LotId":{"name":"lot_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"Release":{"name":"release","in":"path","required":true,"description":"The release number in the lot: 1 for the first; the draft has the next number.","schema":{"type":"integer","minimum":1}},"UploadId":{"name":"upload_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"PageId":{"name":"page_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"WebhookId":{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"HandoverId":{"name":"handover_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"FileId":{"name":"file_id","in":"path","required":true,"description":"A factory file (§6e)","schema":{"type":"string","format":"uuid"}},"DeliveryId":{"name":"delivery_id","in":"path","required":true,"description":"A delivery (§9c)","schema":{"type":"string","format":"uuid"}},"Batch":{"name":"batch","in":"path","required":true,"description":"A batch code (GS1 AI 10): 1 to 20 characters of GS1 character set 82, percent-encoded in the path (/ as %2F, % as %25); anything else names no batch (404)","schema":{"type":"string","minLength":1,"maxLength":20}},"ClientVersion":{"name":"DPP-Client-Version","in":"header","description":"The connector's version, shown on the Connections page (a malformed value is ignored).","schema":{"type":"string","pattern":"^[A-Za-z0-9._+-]{1,40}$"}}},"headers":{"ETag":{"schema":{"type":"string"}},"RetryAfter":{"schema":{"type":"integer","minimum":0},"description":"Seconds to wait before retrying."}},"responses":{"Problem":{"description":"An error (RFC 9457)","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"BalanceTooLow":{"description":"balance_too_low: the prepaid balance does not cover the new passports (section 8). Nothing was published or requested; a person tops up at top_up_url, then the same request goes through.\n","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/BalanceTooLow"}}}}},"schemas":{"Problem":{"type":"object","required":["type","title","status","code"],"properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string","description":"Stable machine-readable code. Branch on this."},"detail":{"type":"string"},"request_id":{"type":"string"},"missing_scope":{"type":"string","description":"insufficient_scope: the scope the request needs."},"errors":{"type":"array","items":{"$ref":"#/components/schemas/Issue"}},"terms_version":{"type":"string","description":"terms_outdated: the Retailer Terms version an admin of the organisation accepts first (§9b, §9c)."},"file_id":{"type":"string","format":"uuid","description":"file_open: the open factory file of the batch (§6e)."},"draft_hash":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"draft_changed: the hash of the draft as it is now."},"needed":{"type":"integer","minimum":1,"description":"balance_too_low: what the prepaid balance lacks, in the smallest unit of currency (cents)."},"price":{"type":"integer","minimum":0,"description":"balance_too_low: what the new passports cost, in cents."},"balance":{"type":"integer","description":"balance_too_low: the prepaid balance, in cents (below zero after a refund or a dispute)."},"currency":{"type":"string","pattern":"^[a-z]{3}$","examples":["eur"],"description":"balance_too_low: the currency of the amounts."},"top_up_url":{"type":"string","format":"uri","description":"balance_too_low: the dashboard's Billing page, where a person tops up the balance."},"verify_url":{"type":"string","format":"uri","description":"org_not_verified and draft_limit_reached: the dashboard's Organisation settings, where an owner or admin verifies the business (DPP Host checks it)."},"claim_url":{"type":"string","format":"uri","description":"gtin_not_claimed: the dashboard's Organisation settings, GTINs, where a person claims the GTIN (DPP Host checks the claim)."},"plans_url":{"type":"string","format":"uri","description":"passport_limit and premium_required: the dashboard's Billing, where an owner or admin pays for more passports or chooses Premium."},"problems":{"type":"array","description":"validation_failed of PUT /pages/{page_id}/blocks: every problem of the content (the first in detail).","items":{"$ref":"#/components/schemas/PageProblem"}},"blocked":{"type":"array","items":{"type":"object"},"description":"release_blocked: every reason the release checks block the page."},"warnings":{"type":"array","items":{"type":"object"},"description":"release_blocked: what the release checks warn about."},"rejected":{"type":"array","description":"validation_failed of POST /connection/sync: every row, none could be queued.","items":{"$ref":"#/components/schemas/ConnectionSyncRejectedRow"}}}},"BalanceTooLow":{"description":"A Problem with code balance_too_low and status 402, with the numbers in the smallest unit of currency (cents).","allOf":[{"$ref":"#/components/schemas/Problem"},{"type":"object","required":["needed","price","balance","currency"],"properties":{"code":{"const":"balance_too_low"},"status":{"const":402}}}]},"Issue":{"type":"object","required":["code","message"],"properties":{"field_key":{"type":["string","null"]},"code":{"type":"string"},"message":{"type":"string"},"item":{"type":"integer","minimum":1,"description":"POST /passports/bulk and POST /uploads/{upload_id}/serials: the item, by its number in the request (from 1)."},"range":{"type":"integer","minimum":1,"description":"POST /uploads/{upload_id}/serials: the range, by its number in the request (from 1)."},"state":{"type":"integer","minimum":1,"description":"POST /lots/{lot_id}/serial-states: the state, by its number in the request (from 1)."},"serial":{"type":["string","null"],"description":"The item's serial, or the range as prefix{from..to}."},"index":{"type":"integer","minimum":0,"description":"A handover's or a delivery's item, by its place in items (from 0; §9b, §9c)."},"id":{"type":"string","format":"uuid","description":"A delivery's stock item (§9c)."},"gtin":{"type":"string","description":"A handover's or a delivery's item: its GTIN as the request gave it."},"ask":{"type":"object","description":"not_in_stock: the business that supplies the shop (§9b).","properties":{"name":{"type":"string"}}},"notice_id":{"type":"string","format":"uuid","description":"recall: the published recall that applies to the item (§9b)."},"part":{"type":"boolean","description":"The item is a battery of a bike in the request (§9b)."},"reason":{"type":"string","description":"An import row's refused value: its reason code (docs/product/typed-fields.md)."}}},"PageProblem":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","examples":["schema","too_large","duplicate_block_id","locale_not_published","text_blank","default_locale_missing","field_unknown","media_unknown","link_invalid"]},"message":{"type":"string"},"block_id":{"type":"string"},"field":{"type":"string"},"locale":{"type":"string"},"detail":{"type":"object","description":"The problem's facts: bytes and max, field_key, media_id, host, hosts."}}},"Page":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{}},"next_cursor":{"type":["string","null"]}}},"Gtin":{"type":"string","pattern":"^([0-9]{8}|[0-9]{12,14})$","description":"GTIN-8, -12, -13 or -14 with a valid check digit. Resolver paths use the 14-digit form."},"DppHostId":{"type":"string","pattern":"^[A-HJ-NP-Z][2-9A-HJ-NP-Z]{11}$","readOnly":true,"examples":["K7M4QXR2WPDL"],"description":"A DPP Host ID: 12 characters of GS1 character set 32 (no 0, 1, I or O), a letter first, the last two a GS1 check character pair that the server checks. DPP Host issues it for a size without a GTIN under a rule set whose requires_gtin is false; nobody sets or changes it. It is not a GTIN, not a GS1 key and not the ESPR unique product identifier. People see it as K7M4-QXR2-WPDL; IDs starting with Z are demo IDs.\n"},"Channel":{"type":"string","enum":["dashboard","api","mcp","system"]},"ContentLocale":{"type":"string","description":"A page language (the 24 official EU languages and Simplified Chinese).","enum":["en","de","fr","es","it","nl","pl","da","bg","cs","el","et","fi","ga","hr","hu","lt","lv","mt","pt","ro","sk","sl","sv","zh-Hans"]},"Scope":{"type":"string","description":"The catalogue of API key scopes (public.api_scopes, §4).","enum":["passports:read","passports:write","passports:publish","imports:write","media:write","pages:write","pages:release","audit:read","webhooks:manage","owners:read","owners:write","handovers:write","stock:read","stock:write"]},"Me":{"type":"object","properties":{"principal":{"type":"object","properties":{"kind":{"type":"string","enum":["api_key"]},"key_id":{"type":"string","format":"uuid"},"key_prefix":{"type":"string","pattern":"^[a-z2-7]{8}$"},"name":{"type":"string"},"scopes":{"type":"array","description":"The key's scopes now (capped by its creator's current role).","items":{"$ref":"#/components/schemas/Scope"}},"may_confirm":{"type":"boolean","description":"The key holds passports:publish and may confirm values."},"publishes_unattended":{"type":"boolean","description":"passports:publish in a verified organisation."},"expires_at":{"type":"string","format":"date-time"}}},"organization":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"plan":{"type":"string","examples":["free","premium","enterprise"]},"status":{"type":"string","examples":["active","past_due","read_only"]},"verified":{"type":"boolean"},"requires_verification":{"type":"boolean"}}},"limits":{"type":"object","description":"The organisation's effective limits, from the plans table (null = no limit).","properties":{"api_rpm":{"type":["integer","null"]},"api_daily":{"type":["integer","null"]},"publishes_per_day":{"type":["integer","null"]},"max_version_bytes":{"type":["integer","null"]},"max_languages":{"type":["integer","null"]}}},"usage":{"type":"object","properties":{"requests_today":{"type":"integer","description":"Requests of the organisation today (UTC), all keys together."}}},"balance":{"type":"object","description":"The prepaid balance (docs/billing/prepaid-balance.md): a passport's first publication is paid from it unless a monthly subscription pays the passports. Amounts in the smallest unit of currency (cents). A person tops it up in the dashboard (top_up_url).\n","required":["pays_from_balance","cents","currency","next_passport_cents","covers_next","top_up_url"],"properties":{"pays_from_balance":{"type":"boolean","description":"False while a monthly subscription pays the passports (EUR 0.10 a month each in its first 12 months)."},"cents":{"type":"integer","description":"The balance; below zero after a refund or a dispute of a top-up."},"currency":{"type":"string","pattern":"^[a-z]{3}$","examples":["eur"]},"next_passport_cents":{"type":["integer","null"],"minimum":0,"description":"What one more passport costs now: 0 when it is free or not paid from the balance; null when the plan has no price in the currency."},"covers_next":{"type":"boolean","description":"The balance covers the next passport."},"top_up_url":{"type":"string","format":"uri","description":"The dashboard's Billing page, where a person tops up."}}}}},"RuleSet":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product_group":{"type":"string","examples":["general"]},"version":{"type":"string","examples":["1"]},"status":{"type":"string","enum":["draft","active","retired"]},"effective_from":{"type":["string","null"],"format":"date"},"legal_basis":{"type":["string","null"]},"requires_gtin":{"type":"boolean","description":"True when a passport under this rule set needs a GTIN. False: a size without a GTIN gets a DPP Host ID at its first passport."},"availability":{"type":"string","enum":["off","pilot","on"],"description":"Who may pick the set: on, every organisation; pilot, the organisations staff made pilots; off, none (never listed)."},"levels":{"type":"array","items":{"type":"string","enum":["model","batch","item"]},"description":"The passport levels the set allows."},"scope_when":{"type":["object","null"],"description":"The condition on the classifier fields a product must meet (docs/product/product-groups.md §5.2); null for any product."},"source_acts":{"type":"array","items":{"type":"string","examples":["32023R1542"]},"description":"CELEX numbers of the acts the set implements, its own act first."},"supersedes":{"type":["string","null"],"format":"uuid","description":"The previous version of the product group."},"keep_online":{"type":["string","null"],"examples":["until_recycled","expected_lifetime","15"],"description":"How long a passport stays online: until_recycled, expected_lifetime or a number of years."},"reviewed_on":{"type":["string","null"],"format":"date","description":"When the set was last checked against its acts."},"created_at":{"type":"string","format":"date-time"}}},"RuleSetDetail":{"allOf":[{"$ref":"#/components/schemas/RuleSet"},{"type":"object","properties":{"carrier_profile":{"type":["object","null"],"description":"Where the QR code goes and how; null when the set gives none."},"fields":{"type":"array","description":"In the rule set's order.","items":{"type":"object","properties":{"field_key":{"type":"string"},"label":{"type":"string"},"field_group":{"type":"string"},"section":{"type":"string","description":"The page section the field's group shows in."},"data_type":{"type":"string","enum":["text","number","percent","country","date","json","file","boolean"]},"format":{"type":["string","null"],"examples":["term","measurement","record"],"description":"validation.format (docs/product/typed-fields.md); null for a plain value of its data type."},"term_lists":{"type":"array","items":{"type":"string"},"description":"The term lists whose codes its values take (GET /rule-sets/{rule_set_id}/terms)."},"expected":{"type":"string","enum":["core","likely","optional"]},"access":{"type":"array","items":{"type":"string","enum":["public","legitimate_interest","authorities","notified_bodies","commission","customs","internal"]},"description":"Who may read the value: public alone, or any of the others. Only public fields are published; the others stay in the drafts and the export."},"access_roles":{"type":"array","items":{"type":"string"},"description":"The roles that count as a legitimate interest (codes of the term list access_role)."},"visibility":{"type":"string","enum":["public","restricted","authorities"],"description":"The older summary of access."},"data_level":{"type":"string","enum":["model","lot","item"],"description":"Where the value is set: the product or a size, a lot, or an item passport (docs/api/v1.md §6a)."},"changes":{"type":"string","enum":["static","event","use"],"description":"static; event: set at placing on the market and at a status change; use: readings during use."},"classifier":{"type":"boolean","description":"A fact the conditions read (applies_when, scope_when); never published."},"applies_from":{"type":["string","null"],"description":"The code of the legal date from which the field applies; null: with the rule set."},"applies_from_date":{"type":["string","null"],"format":"date","description":"That date; null while it waits for an EU act."},"applies_when":{"type":["object","null"],"description":"A condition on the classifiers (docs/product/product-groups.md §5.2); null: always."},"applies_from_cases":{"type":"array","description":"Other legal dates for some products, in order: the first case whose condition holds on the product's classifiers gives the date instead of applies_from (docs/product/product-groups.md §5.5); [] for most fields.","items":{"type":"object","properties":{"applies_from":{"type":"string","description":"The code of the legal date."},"applies_from_date":{"type":["string","null"],"format":"date","description":"That date; null while it waits for an EU act."},"when":{"type":"object","description":"A condition on the classifiers."}}}},"legal_refs":{"type":"array","description":"Where the field comes from.","items":{"type":"object","properties":{"celex":{"type":"string","examples":["32023R1542"]},"ref":{"type":"string","examples":["Annex XIII 1(h)"]}}}},"semantic_ids":{"type":"object","description":"The field's ids in other dictionaries."},"key_fact":{"type":["integer","null"],"minimum":1,"maximum":4,"description":"Its place in the page's key facts."},"unit":{"type":["object","null"],"description":"The unit of the value (always this unit; the server never converts); null without one.","properties":{"unit":{"type":"string","examples":["Ah"]},"ucum":{"type":["string","null"],"examples":["A.h"]},"unece_code":{"type":["string","null"],"examples":["AMH"]}}},"best_source":{"type":["string","null"]},"server_set":{"type":["boolean","null"],"description":"true: set by the server at publish (product_uid, passport_version, backup_provider), refused in a draft; null for a field without a best_source."},"validation":{"type":["object","null"]},"legal_basis":{"type":["string","null"]}}}}}}]},"RuleSetTerms":{"type":"object","properties":{"rule_set_id":{"type":"string","format":"uuid"},"product_group":{"type":"string"},"version":{"type":"string"},"lang":{"$ref":"#/components/schemas/ContentLocale"},"lists":{"type":"array","items":{"$ref":"#/components/schemas/TermList"}}}},"TermList":{"type":"object","properties":{"list":{"type":"string","examples":["battery_chemistry"]},"lang":{"allOf":[{"$ref":"#/components/schemas/ContentLocale"}],"description":"The language of the labels; en when the list has no label for every code in the language asked for."},"fields":{"type":"array","items":{"type":"string"},"description":"The fields whose values take codes from the list (for access_role, the fields that name roles)."},"terms":{"type":"array","description":"The list whole, in its order; empty for a list that waits for its act.","items":{"type":"object","properties":{"code":{"type":"string","description":"What a value stores."},"label":{"type":"string"},"group":{"type":["string","null"],"description":"The term's group where the list has groups (a care symbol's treatment, a fibre's origin)."}}}}}},"Source":{"type":"string","description":"The source a request names for its values or records. brand (typed by a person of the brand) only from a key with passports:publish; api is an integration's own value. The server's sources (ValueSource) are refused.\n","enum":["shop_scrape","csv","ai_docs","supplier","brand","certificate","api"]},"ValueSource":{"type":"string","description":"Where a stored value or record came from (public.value_source): the sources a request may name (Source), and import (an import's suggestion), shop (a shop connection's), calculated and system (the server's own).\n","enum":["shop_scrape","csv","ai_docs","supplier","brand","certificate","calculated","system","api","import","shop"]},"ProductCreate":{"type":"object","required":["title"],"additionalProperties":false,"properties":{"title":{"type":"string","minLength":1,"maxLength":500},"category":{"type":["string","null"],"maxLength":200},"external_id":{"type":["string","null"],"maxLength":200,"description":"Your own id; unique per organisation."},"rule_set_id":{"type":["string","null"],"format":"uuid"},"image_url":{"type":["string","null"],"format":"uri","pattern":"^https://","maxLength":2000},"shop_url":{"type":["string","null"],"format":"uri","pattern":"^https://","maxLength":2000},"source":{"allOf":[{"$ref":"#/components/schemas/Source"}],"description":"Default brand for a key with passports:publish, api for any other key."}}},"ProductUpdate":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"title":{"type":"string","minLength":1,"maxLength":500},"category":{"type":["string","null"],"maxLength":200},"external_id":{"type":["string","null"],"maxLength":200},"rule_set_id":{"type":["string","null"],"format":"uuid","description":"A product with passports keeps a rule set."},"image_url":{"type":["string","null"],"format":"uri","pattern":"^https://","maxLength":2000},"shop_url":{"type":["string","null"],"format":"uri","pattern":"^https://","maxLength":2000}}},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"category":{"type":["string","null"]},"external_id":{"type":["string","null"]},"rule_set_id":{"type":["string","null"],"format":"uuid"},"image_url":{"type":["string","null"]},"shop_url":{"type":["string","null"]},"source":{"$ref":"#/components/schemas/ValueSource"},"record_confirmed":{"type":"boolean","description":"false while the title or category were last written by a key without passports:publish (or through MCP): they stay out of the passport until a person confirms them.\n"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"VariantWrite":{"type":"object","additionalProperties":false,"properties":{"sku":{"type":["string","null"],"maxLength":100},"gtin":{"oneOf":[{"$ref":"#/components/schemas/Gtin"},{"type":"null"}],"description":"A global GTIN (not restricted-circulation, coupon or all-zero); one variant per GTIN per organisation."},"title":{"type":["string","null"],"maxLength":500},"external_id":{"type":["string","null"],"maxLength":200}}},"Variant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"gtin":{"type":["string","null"]},"dpphost_id":{"$ref":"#/components/schemas/DppHostId","description":"Only on a size that has a DPP Host ID (then gtin is null). Read-only; not a member of POST or PATCH."},"sku":{"type":["string","null"]},"title":{"type":["string","null"]},"external_id":{"type":["string","null"]},"record_confirmed":{"type":"boolean","description":"false while the GTIN or SKU were last written by a key without passports:publish."},"created_at":{"type":"string","format":"date-time"}}},"Lot":{"type":"object","description":"A production batch of a size (docs/api/v1.md §6a).","properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"variant_id":{"type":"string","format":"uuid"},"code":{"type":"string","description":"The GS1 batch/lot code (AI 10), unique in the size."},"record_confirmed":{"type":"boolean","description":"false while a person has not confirmed a lot that a key without passports:publish or an MCP client made."},"created_at":{"type":"string","format":"date-time"},"passports":{"type":"integer","minimum":0,"description":"How many passports belong to the lot."},"upi_form":{"type":"string","enum":["with_lot","without_lot"],"description":"The URL form of the lot's synthesised items (manifest lots, docs/product/sparse-items.md §1.3): fixed at the first release."},"uploads":{"type":"integer","minimum":0,"description":"Serial uploads of the lot that have not failed (sparse items)."},"segments":{"type":"integer","minimum":0,"description":"Segments of the lot with their second copy (ready uploads)."},"listed":{"type":"integer","minimum":0,"description":"The serials those segments list, in lists and ranges."},"current_release":{"type":"integer","minimum":0,"description":"The number of the lot's current published release; 0 before its first (sparse items S3)."},"releases":{"type":"integer","minimum":0,"description":"How many releases of the lot are published."},"draft_release":{"type":["string","null"],"format":"uuid","description":"The id of the lot's draft release, or null (one draft per lot)."},"pending_states":{"type":"integer","minimum":0,"description":"Serial states recorded for the next release (§6d)."},"materialised":{"type":"integer","minimum":0,"description":"Serials of the lot that got a passport of their own (sparse items S9)."}}},"LotCreate":{"type":"object","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","minLength":1,"maxLength":20,"description":"The GS1 batch/lot code: 1 to 20 characters of GS1 character set 82, never . or .."}}},"LotPatch":{"type":"object","required":["upi_form"],"additionalProperties":false,"properties":{"upi_form":{"type":"string","enum":["with_lot","without_lot"]}}},"LotUploadDefaults":{"type":"object","description":"What every serial of the upload has unless its own entry says (docs/product/sparse-items.md §2.3).","additionalProperties":false,"properties":{"placed_on_market_on":{"type":["string","null"],"format":"date","description":"Today or up to a year ago."},"fields":{"type":["object","null"],"description":"Public item values a manifest may hold, by field key (for batteries/draft-1: manufacturing_date). 422 field_not_in_manifest for any other field.","additionalProperties":true}}},"LotUploadCreate":{"type":"object","additionalProperties":false,"properties":{"defaults":{"$ref":"#/components/schemas/LotUploadDefaults"}}},"LotUploadItem":{"type":"object","required":["serial"],"additionalProperties":false,"properties":{"serial":{"type":"string","minLength":1,"maxLength":20,"description":"1 to 20 characters of GS1 character set 82, never . or .."},"placed_on_market_on":{"type":["string","null"],"format":"date"},"fields":{"type":["object","null"],"additionalProperties":true,"description":"The serial's own public item values (in_manifest fields)."}}},"LotUploadRange":{"type":"object","required":["prefix","from","to"],"additionalProperties":false,"description":"A run of consecutive numeric serials: prefix, then the counter zero-padded to the width of from, from from to to (both inclusive).","properties":{"prefix":{"type":"string","maxLength":19,"description":"May be empty; GS1 character set 82. Prefix and width together at most 20 characters."},"from":{"type":"string","pattern":"^[0-9]{1,20}$"},"to":{"type":"string","pattern":"^[0-9]{1,20}$","description":"As many digits as from, not below it."},"width":{"type":"integer","minimum":1,"maximum":20,"description":"Optional; must equal the number of digits of from."},"placed_on_market_on":{"type":["string","null"],"format":"date"},"fields":{"type":["object","null"],"additionalProperties":true}}},"LotUploadStage":{"type":"object","additionalProperties":false,"description":"items, ranges or both.","properties":{"items":{"type":"array","minItems":1,"maxItems":100000,"items":{"$ref":"#/components/schemas/LotUploadItem"}},"ranges":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/LotUploadRange"}}}},"LotSegment":{"type":"object","description":"The immutable segment built from an upload (docs/product/sparse-items.md §2.2, §2.6): metadata only, the serials live in the object m/{sha256}.seg.","properties":{"id":{"type":"string","format":"uuid"},"sha256":{"type":"string","description":"SHA-256 of the object, hex: its name."},"root":{"type":"string","description":"The segment root (dpphost-merkle/1 over its blocks, ranges and header), hex."},"r2_key":{"type":"string"},"bytes":{"type":"integer"},"serials":{"type":"integer","description":"Serials listed one by one."},"range_serials":{"type":"integer","description":"Serials covered by ranges."},"ranges":{"type":"integer"},"blocks":{"type":"integer"},"first_serial":{"type":"string"},"last_serial":{"type":"string"},"index_offset":{"type":"integer"},"index_length":{"type":"integer","description":"The bytes a reader fetches for the directory (header, ranges, block index)."},"columns":{"type":"array","items":{"type":"string"}},"defaults":{"type":"object","additionalProperties":true,"description":"The segment's defaults per column."},"built_at":{"type":"string","format":"date-time"},"mirrored_at":{"type":["string","null"],"format":"date-time","description":"When the second copy was checked; null until then."}}},"LotUpload":{"type":"object","description":"One batch of serials for a manifest lot (docs/product/sparse-items.md §5.2).","properties":{"id":{"type":"string","format":"uuid"},"lot_id":{"type":"string","format":"uuid"},"variant_id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["open","queued","building","built","ready","failed"],"description":"open (taking serials), queued (closed, waiting for its build), building, built (the segment is written, waiting for its second copy), ready, failed (error and problems say why)."},"defaults":{"$ref":"#/components/schemas/LotUploadDefaults"},"serials":{"type":"integer","description":"Serials staged one by one."},"ranges":{"type":"integer"},"range_serials":{"type":"integer","description":"Serials covered by the staged ranges."},"attempts":{"type":"integer"},"error":{"type":["string","null"]},"problems":{"type":["array","null"],"description":"The first 100 problems of a failed build: serial_taken (listed in another segment of the size) or missing_item_value (a required public item value missing after the defaults).","items":{"type":"object","properties":{"code":{"type":"string","enum":["serial_taken","missing_item_value"]},"serial":{"type":"string","description":"The serial, or the range as prefix{from..to}."}}}},"problem_counts":{"type":["object","null"],"additionalProperties":{"type":"integer"}},"created_at":{"type":"string","format":"date-time"},"closed_at":{"type":["string","null"],"format":"date-time"},"built_at":{"type":["string","null"],"format":"date-time"},"ready_at":{"type":["string","null"],"format":"date-time"},"failed_at":{"type":["string","null"],"format":"date-time"},"segment":{"oneOf":[{"$ref":"#/components/schemas/LotSegment"},{"type":"null"}]}}},"LotRelease":{"type":"object","description":"A numbered, immutable state of a manifest lot (docs/product/sparse-items.md §2.5, §5.3): its public lot values, segments, serial states, model link and defaults. Published through POST /lots/{lot_id}/releases/{release}/publish.","properties":{"id":{"type":"string","format":"uuid"},"lot_id":{"type":"string","format":"uuid"},"variant_id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"release":{"type":"integer","minimum":1,"description":"Its number in the lot."},"status":{"type":"string","enum":["draft","published"]},"scope":{"type":["string","null"],"enum":["lot","serials",null],"description":"Once published: lot (every listed serial gets a version) or serials (only the serials it adds or names). Null for a draft."},"day":{"type":"string","format":"date","description":"The day it was published (a draft: the day it was drafted)."},"content_hash":{"type":["string","null"],"pattern":"^[0-9a-f]{64}$","description":"SHA-256 of the JCS of data, once published."},"model":{"type":["object","null"],"description":"The model passport version every item of the lot links.","properties":{"passport_id":{"type":"string","format":"uuid"},"version":{"type":"integer"}}},"segments":{"type":"integer","minimum":0,"description":"Segments the release lists (cumulative)."},"segments_new":{"type":"integer","minimum":0,"description":"Segments it adds."},"serials":{"type":"object","properties":{"total":{"type":"integer","minimum":0},"new":{"type":"integer","minimum":0}}},"states":{"type":"integer","minimum":0,"description":"Serial states it carries."},"states_mode":{"type":"string","enum":["inline","segment"],"description":"Where its states live (sparse items S9): inline in its object, or in a state segment of their own once the inline states would pass the cap."},"state_segment_ready":{"type":["boolean","null"],"description":"For states_mode segment: whether the state segment is built and has its second copy (the release publishes only then; the gap check says state_segment_pending before). Null for inline."},"defaults":{"$ref":"#/components/schemas/LotUploadDefaults"},"languages":{"type":"array","items":{"type":"string"}},"page_tier":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"created_via":{"type":"string","enum":["user","api_key","mcp_grant"]},"published_at":{"type":["string","null"],"format":"date-time"},"published_via":{"type":["string","null"],"enum":["user","api_key","relink",null],"description":"A person through an approval, a passports:publish key, or the relink job after a new model version."},"approval_id":{"type":["string","null"],"format":"uuid"},"backup_ref":{"type":["string","null"],"description":"r2:s/{lot_id}/{n}.json once published."},"data":{"type":["object","null"],"additionalProperties":true,"description":"GET …/releases/{release} only: the release object dpphost.lot-release/1, what the resolver serves at s/{lot_id}/{n}.json once published. Never a restricted value."},"gap_check":{"$ref":"#/components/schemas/LotValidationResult"}}},"LotReleaseCreate":{"type":"object","additionalProperties":false,"properties":{"uploads":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"string","format":"uuid"},"description":"Ready uploads of the lot to list. Left out: every ready upload not yet released."},"defaults":{"$ref":"#/components/schemas/LotUploadDefaults"}}},"LotValidationResult":{"allOf":[{"$ref":"#/components/schemas/ValidationResult"},{"type":"object","description":"The gap check per lot (docs/product/sparse-items.md §5.3): the checks of a passport once per lot, judged once per distinct placing date of the release. No draft_hash, passport_id or resolver_path.","properties":{"lot_id":{"type":"string","format":"uuid"},"release":{"type":"integer","minimum":1,"description":"The release the check is for: the draft's number, or the next."},"release_id":{"type":["string","null"],"format":"uuid"},"dates":{"type":"array","items":{"type":"string","format":"date"},"description":"The distinct placing dates judged."},"missing_detail":{"type":"array","description":"Per missing field: the dates it applies on and the serials without a value.","items":{"type":"object","properties":{"field_key":{"type":"string"},"dates":{"type":"array","items":{"type":"string","format":"date"}},"serials":{"type":"integer","minimum":0}}}},"serials":{"type":"object","properties":{"listed":{"type":"integer","minimum":0},"new":{"type":"integer","minimum":0}}},"materialise_on":{"type":"array","items":{"type":"string"},"description":"Public item fields no serial list can hold that block nothing: a value for one item is set on that item's own passport, which hands it over (docs/product/sparse-items.md §2.7, C-12). For batteries/draft-1: predecessor_passports."}}}]},"LotReleasePublishResult":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["published","approval_required"]},"release":{"$ref":"#/components/schemas/LotRelease"},"approval":{"$ref":"#/components/schemas/Approval"}}},"MerklePathStep":{"type":"object","required":["side","hash"],"properties":{"side":{"type":"string","enum":["L","R"]},"hash":{"type":"string","pattern":"^[0-9a-f]{64}$"}}},"SerialProof":{"type":"object","description":"The proof of one listed serial (docs/product/sparse-items.md §4.2): the entry part of the item's ?format=proof answer, as the resolver gives it. Postgres checks it against the roots of the lot's released segments.","required":["line","header","leaf","block","segment"],"properties":{"line":{"type":"string","description":"The entry's canonical line: the serial (or the range) and its own values."},"header":{"type":"object","additionalProperties":true,"description":"The segment's header, salt included."},"leaf":{"type":"string","pattern":"^[0-9a-f]{64}$"},"block":{"type":["object","null"],"description":"A list entry's block: its root and the path from the entry's leaf to it. Null for a range entry.","properties":{"root":{"type":"string","pattern":"^[0-9a-f]{64}$"},"path":{"type":"array","items":{"$ref":"#/components/schemas/MerklePathStep"}}}},"segment":{"type":"object","properties":{"root":{"type":"string","pattern":"^[0-9a-f]{64}$"},"path":{"type":"array","items":{"$ref":"#/components/schemas/MerklePathStep"}}}}}},"SerialState":{"type":"object","required":["serial","state","proof"],"additionalProperties":false,"properties":{"serial":{"type":"string","minLength":1,"maxLength":20},"state":{"type":"string","enum":["gone","override"],"description":"withdrawn and listed are recorded in the dashboard (403 dashboard_only); materialised is the system's (403 system_only)."},"proof":{"$ref":"#/components/schemas/SerialProof"},"reason":{"type":"string","maxLength":500,"description":"gone: recycled or listed_in_error (moderation and court_order are staff decisions). override: why, optional."},"field":{"type":"string","description":"override: an in_manifest field of the rule set, never placed_on_market_on."},"value":{"description":"override: the new value, checked as the field's type."}}},"SerialStates":{"type":"object","required":["states"],"additionalProperties":false,"properties":{"states":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/SerialState"}}}},"SerialStatesResult":{"type":"object","properties":{"added":{"type":"integer","minimum":0},"pending":{"type":"integer","minimum":0,"description":"States waiting for the lot's next release."}}},"LotItemValue":{"type":"object","required":["serial","value","proof"],"additionalProperties":false,"properties":{"serial":{"type":"string","minLength":1,"maxLength":20},"value":{"description":"The serial's own value, checked as the field's type; null removes the exception."},"proof":{"$ref":"#/components/schemas/SerialProof"}}},"LotItemValues":{"type":"object","required":["field_key"],"additionalProperties":false,"properties":{"field_key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,99}$","description":"A restricted item field of the lot's rule set."},"default":{"description":"The value every listed serial has unless its own says; checked as the field's type."},"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/LotItemValue"}},"clear_default":{"type":"boolean","default":false,"description":"Remove the lot default (not with default)."}}},"LotItemValuesResult":{"type":"object","description":"Counts only; the values are never answered.","properties":{"field_key":{"type":"string"},"default":{"type":"boolean","description":"Whether the lot has a default for the field."},"items":{"type":"integer","minimum":0,"description":"How many serials have a value of their own."}}},"FieldValue":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"field_key":{"type":"string"},"level":{"type":"string","enum":["product","variant","lot","item"]},"variant_id":{"type":["string","null"],"format":"uuid"},"lot_id":{"type":"string","format":"uuid","description":"A lot's value only."},"passport_id":{"type":"string","format":"uuid","description":"An item passport's value only."},"value":{},"status":{"type":"string","enum":["suggested","confirmed","rejected"]},"source":{"$ref":"#/components/schemas/ValueSource"},"channel":{"$ref":"#/components/schemas/Channel"},"confidence":{"type":["number","null"],"minimum":0,"maximum":1},"evidence_ref":{"type":["string","null"]},"provenance":{"type":"object","properties":{"untrusted":{"type":"boolean","description":"Third-party text (supplier, import, shop scrape, AI extraction, certificate)."},"source":{"type":"string"},"source_ref":{"type":["string","null"]}}},"created_at":{"type":"string","format":"date-time"},"confirmed_at":{"type":["string","null"],"format":"date-time"}}},"Draft":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"variant_id":{"type":["string","null"],"format":"uuid"},"lot_id":{"type":"string","format":"uuid","description":"The draft of a lot or of an item passport of a lot only."},"passport_id":{"type":"string","format":"uuid","description":"The draft of an item passport only."},"rule_set_id":{"type":["string","null"],"format":"uuid"},"draft_hash":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"Changes whenever a draft value or a record identifier changes."},"records":{"type":"object","description":"The identifiers the passport takes from the product and variant records.","properties":{"product":{"type":"object","properties":{"title":{"type":"string"},"category":{"type":["string","null"]},"confirmed":{"type":"boolean"}}},"variant":{"type":["object","null"],"properties":{"gtin":{"type":["string","null"]},"sku":{"type":["string","null"]},"confirmed":{"type":"boolean"}}},"lot":{"type":"object","description":"The draft of a lot, or of an item passport of a lot, only.","properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string"},"confirmed":{"type":"boolean"}}},"passport":{"type":"object","description":"The draft of an item passport only.","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["model","batch","item"]},"batch":{"type":"string"},"serial":{"type":"string"},"placed_on_market_on":{"type":"string","format":"date"},"confirmed":{"type":"boolean"}}}}},"fields":{"type":"array","description":"Suggested, confirmed and rejected values (superseded ones are left out). With variant_id, the variant's own values and the product-level ones it falls back to; with lot_id or passport_id, also the lot's and the item passport's own.","items":{"$ref":"#/components/schemas/FieldValue"}}}},"DraftPatch":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"variant_id":{"type":["string","null"],"format":"uuid","description":"Write the variant's values instead of the product's."},"lot_id":{"type":["string","null"],"format":"uuid","description":"Write a lot's values (fields whose data level is lot). Not with passport_id."},"passport_id":{"type":["string","null"],"format":"uuid","description":"Write an item passport's values (fields whose data level is item). Not with lot_id."},"source":{"allOf":[{"$ref":"#/components/schemas/Source"}],"description":"The provenance of the suggested values in changes. Default brand for a key with passports:publish, api for any other key. Confirmed values are always saved with source brand."},"changes":{"type":"array","maxItems":200,"items":{"type":"object","required":["field_key","value"],"additionalProperties":false,"properties":{"field_key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,99}$"},"value":{"description":"The value (checked against the rule set; at most 16 KB). null clears the confirmed value (passports:publish)."},"status":{"type":"string","enum":["suggested","confirmed"],"default":"suggested","description":"confirmed needs passports:publish; the value is saved with source brand, whatever source says."},"confidence":{"type":["number","null"],"minimum":0,"maximum":1},"evidence_ref":{"type":["string","null"],"maxLength":500}}}},"confirm":{"type":"array","maxItems":500,"items":{"type":"string","format":"uuid"},"description":"Suggested value ids to confirm (passports:publish)."},"reject":{"type":"array","maxItems":500,"items":{"type":"string","format":"uuid"},"description":"Suggested value ids to reject (passports:publish)."}}},"DraftPatchResult":{"type":"object","properties":{"applied":{"type":"object","properties":{"cleared":{"type":"integer"},"set":{"type":"array","items":{"type":"object","properties":{"field_key":{"type":"string"},"field_value_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["suggested","confirmed"]}}}},"confirmed":{"type":"integer"},"rejected":{"type":"integer"}}},"draft":{"$ref":"#/components/schemas/Draft"}}},"ValidationIssue":{"type":"object","required":["code","message"],"properties":{"field_key":{"type":["string","null"]},"code":{"type":"string","description":"Issues: no_rule_set, rule_set_retired, gtin_missing, gtin_check_digit, gtin_restricted, gtin_mismatch, sku_mismatch, batch_mismatch, record_unconfirmed, resolver_path_mismatch, resolver_path_taken, rule_set_requires_gtin, invalid_value, fibre_sum, market_electronic_address, market_operator_missing, not_in_scope, wrong_level, model_unpublished, model_version_outdated, placed_on_market_invalid. Warnings: org_unverified, gtin_unclaimed.\n"},"message":{"type":"string"},"detail":{"type":"object"}}},"ValidationResult":{"type":"object","description":"The gap check (docs/api/core-write-path.md §4); publish() runs the same.","properties":{"ok":{"type":"boolean","description":"Nothing missing and no issues. Warnings do not change it."},"draft_hash":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"What a publish by this key would take now; send it as PublishRequest.draft_hash."},"passport_id":{"type":"string","format":"uuid"},"resolver_path":{"type":"string"},"rule_set":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"product_group":{"type":"string"},"version":{"type":"string"},"status":{"type":"string"}}},"core_total":{"type":"integer"},"core_filled":{"type":"integer"},"missing":{"type":"array","items":{"type":"string"},"description":"Core fields without a confirmed value."},"awaiting_confirmation":{"type":"array","items":{"type":"string"},"description":"Missing fields that have a suggestion or an unconfirmed record value."},"applies_later":{"type":"array","description":"Fields whose legal date (the one their cases pick for the product) is after the passport's reference date (the publish date; an item's placing on the market). Never required or published until then.","items":{"type":"object","properties":{"field_key":{"type":"string"},"applies_from":{"type":"string","format":"date"}}}},"pending_act":{"type":"array","description":"Fields whose date waits for an EU act. Never required or published until then.","items":{"type":"object","properties":{"field_key":{"type":"string"},"legal_date":{"type":"string","description":"The code of the legal date (the one the field's cases pick for the product)."}}}},"issues":{"type":"array","items":{"$ref":"#/components/schemas/ValidationIssue"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/ValidationIssue"}}}},"PassportStatus":{"type":"string","enum":["draft","published","withdrawn"]},"PassportCreate":{"type":"object","required":["variant_id"],"additionalProperties":false,"properties":{"variant_id":{"type":"string","format":"uuid","description":"The variant whose GTIN or DPP Host ID the passport identifies."},"product_id":{"type":"string","format":"uuid","description":"Optional check that the variant belongs to this product."},"level":{"type":"string","enum":["model","batch","item"],"default":"model"},"batch_id":{"type":"string","maxLength":20,"description":"GS1 character set 82. Required for level batch; optional for item."},"serial":{"type":"string","maxLength":20,"description":"GS1 character set 82. Required for level item."},"lot":{"type":"string","maxLength":20,"description":"An item passport's lot: its GS1 batch/lot code, found or created (a passport with a batch belongs to the lot of that batch)."},"placed_on_market_on":{"type":"string","format":"date","description":"An item passport's placing on the market: not after today, at most one year ago (422 placed_on_market_invalid)."}}},"PassportUpdate":{"type":"object","required":["placed_on_market_on"],"additionalProperties":false,"properties":{"placed_on_market_on":{"type":["string","null"],"format":"date","description":"An item passport's placing on the market, before its first publish; null clears it."}}},"PassportBulkCreate":{"type":"object","required":["variant_id","items"],"additionalProperties":false,"properties":{"variant_id":{"type":"string","format":"uuid","description":"The size whose item passports these are."},"items":{"type":"array","minItems":1,"maxItems":10000,"items":{"$ref":"#/components/schemas/PassportBulkItem"}},"source":{"allOf":[{"$ref":"#/components/schemas/Source"}],"description":"The source of the item values. Default brand for a key with passports:publish, api for any other key."},"confirm":{"type":"boolean","default":false,"description":"Store the item values as the brand's confirmed values (source brand). Needs passports:publish."}}},"PassportBulkItem":{"type":"object","required":["serial"],"additionalProperties":false,"properties":{"serial":{"type":"string","minLength":1,"maxLength":20,"description":"GS1 character set 82."},"batch_id":{"type":"string","minLength":1,"maxLength":20,"description":"Optional; part of the path (/10/{batch})."},"lot":{"type":"string","minLength":1,"maxLength":20,"description":"The lot the item belongs to, found or created (with batch_id, the batch's lot)."},"placed_on_market_on":{"type":"string","format":"date","description":"Not after today, at most one year ago."},"fields":{"type":"array","minItems":1,"maxItems":200,"description":"The item's values (fields whose data level is item), as in DraftPatch.","items":{"type":"object","required":["field_key","value"],"additionalProperties":false,"properties":{"field_key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,99}$"},"value":{"description":"The value (checked against the rule set; at most 16 KB)."},"confidence":{"type":["number","null"],"minimum":0,"maximum":1},"evidence_ref":{"type":["string","null"],"maxLength":500}}}}}},"PassportBulkResult":{"type":"object","properties":{"created":{"type":"integer"},"lots_created":{"type":"integer"},"values":{"type":"integer","description":"The item values written."},"passports":{"type":"array","description":"In the items' order.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"serial":{"type":"string"},"batch_id":{"type":["string","null"]},"lot":{"type":["string","null"]},"resolver_path":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}},"Passport":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"uid":{"type":"string","pattern":"^[ps]_[0-9a-f]{24}$","description":"p_ and 24 random hex digits; an item passport a manifest lot's serial got (materialised) keeps the uid the lot gave the serial, s_ and the first 24 hex digits of SHA-256(\"dpphost-item-uid/1\" ‖ GTIN-14 ‖ serial).\n"},"product_id":{"type":"string","format":"uuid"},"variant_id":{"type":["string","null"],"format":"uuid"},"level":{"type":"string","enum":["model","batch","item"]},"batch_id":{"type":["string","null"]},"serial":{"type":["string","null"]},"resolver_path":{"type":"string","examples":["/01/09520012345674","/DH/K7M4QXR2WPDL/LOT/2026-03"],"description":"/01/{GTIN-14}[/10/{batch}][/21/{serial}] for a GTIN, or /DH/{DPP Host ID}[/LOT/{batch}][/SER/{serial}] for a DPP Host ID."},"dpphost_id":{"$ref":"#/components/schemas/DppHostId","description":"Only on a DPP Host ID passport. Absent means the passport has a GTIN."},"url":{"type":"string","format":"uri","examples":["https://id.dpphost.eu/01/09520012345674"]},"status":{"$ref":"#/components/schemas/PassportStatus"},"current_version":{"type":"integer","minimum":0},"current":{"oneOf":[{"$ref":"#/components/schemas/PassportVersionSummary"},{"type":"null"}]},"record_confirmed":{"type":"boolean"},"taken_down":{"type":"boolean","description":"Taken down by a moderation action or a GTIN dispute (the address answers 410 or 451)."},"lot_id":{"type":"string","format":"uuid","description":"Only on a passport that belongs to a lot."},"lot":{"type":"string","description":"The lot's code; only with lot_id."},"placed_on_market_on":{"type":"string","format":"date","description":"Only on an item passport whose placing on the market is set."},"created_at":{"type":"string","format":"date-time"}}},"PassportVersionSummary":{"type":"object","properties":{"version":{"type":"integer","minimum":1},"rule_set_id":{"type":"string","format":"uuid"},"content_hash":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"SHA-256 over the JCS (RFC 8785) form of the data as published."},"published_at":{"type":"string","format":"date-time"},"withheld":{"type":"boolean","description":"The whole version is withheld from the public (DSA)."},"redacted":{"type":"boolean","description":"Fields or the whole version are withheld from the public (DSA)."}}},"PassportVersion":{"allOf":[{"$ref":"#/components/schemas/PassportVersionSummary"},{"type":"object","properties":{"data":{"type":"object","description":"The version as stored (the organisation's own record)."},"jsonld":{"type":"object"},"redactions":{"type":"array","description":"What the public output withholds (docs/compliance/dsa.md §2).","items":{"type":"object","properties":{"scope":{"type":"string","enum":["fields","version"]},"field_keys":{"type":["array","null"],"items":{"type":"string"}},"all_versions":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}}}}}]},"PublishRequest":{"type":"object","additionalProperties":false,"properties":{"draft_hash":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"From POST …/validate. Refuse with 409 draft_changed if the draft changed since."}}},"PublishResult":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["published","approval_required"]},"version":{"$ref":"#/components/schemas/PassportVersionSummary"},"passport":{"$ref":"#/components/schemas/Passport"},"approval":{"$ref":"#/components/schemas/Approval"}}},"Approval":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["passport_publish","page_release","lot_release"],"description":"lot_release since sparse items S6 (§6d): a request to publish a release of a manifest lot."},"status":{"type":"string","enum":["pending","approved","rejected","expired","used"]},"passport_id":{"type":["string","null"],"format":"uuid"},"page_id":{"type":["string","null"],"format":"uuid"},"page_version":{"type":["integer","null"],"description":"page_release only; a rollback to this version (null the draft)."},"draft_revision":{"type":["integer","null"],"description":"page_release only; the draft revision to release."},"snapshot_hash":{"type":"string"},"requested_via":{"type":"string","enum":["user","api_key","mcp_grant"],"description":"Who asked: a person in the dashboard, an API key without passports:publish, or an MCP client."},"requested_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"approve_url":{"type":"string","format":"uri","examples":["https://app.dpphost.eu/approve/…"]},"decided_at":{"type":["string","null"],"format":"date-time"},"result_version":{"type":["integer","null"]},"lot_id":{"type":["string","null"],"format":"uuid","description":"lot_release only."},"release_id":{"type":["string","null"],"format":"uuid","description":"lot_release only."},"release":{"type":["integer","null"],"description":"lot_release only: the release number the request is for."},"result_release":{"type":["integer","null"],"description":"lot_release only: the release number once published."},"needs_confirmation":{"type":"integer","description":"How many values and records the person confirms one by one."}}},"ImportCreate":{"type":"object","required":["rows"],"additionalProperties":false,"properties":{"rows":{"type":"array","minItems":1,"maxItems":5000,"items":{"$ref":"#/components/schemas/ImportRowInput"}},"rule_set_id":{"type":"string","format":"uuid","description":"The rule set of new products (a row may name its own)."},"confirm":{"type":"boolean","default":false,"description":"Store values as confirmed, as the brand's (source brand). Needs passports:publish."}}},"ImportRowInput":{"type":"object","additionalProperties":false,"description":"One product, optionally one variant and one passport draft, with field values. The product is found by external_id, else by the GTIN of one of its variants, and created when neither finds one (then title is required). Values are suggestions with source import unless the job confirms.\n","properties":{"external_id":{"type":["string","null"],"maxLength":200},"title":{"type":["string","null"],"maxLength":500},"category":{"type":["string","null"],"maxLength":200},"rule_set_id":{"type":["string","null"],"format":"uuid"},"image_url":{"type":["string","null"]},"shop_url":{"type":["string","null"]},"gtin":{"type":["string","null"],"description":"The variant (found or created); a passport's GTIN."},"sku":{"type":["string","null"]},"variant_title":{"type":["string","null"]},"variant_external_id":{"type":["string","null"]},"level":{"type":["string","null"],"enum":["model","batch","item",null],"description":"Creates a passport draft when none exists for these identifiers."},"batch_id":{"type":["string","null"]},"serial":{"type":["string","null"]},"lot":{"type":["string","null"],"description":"The lot (GS1 batch/lot code) the row's item passport belongs to, found or created; without passport columns the row makes or finds the lot only. A passport with a batch belongs to the lot of that batch."},"placed_on_market_on":{"type":["string","null"],"format":"date","description":"An item passport's placing on the market: not after today, at most one year ago."},"fields":{"type":"object","description":"Product-level values by field key (null values are skipped)."},"variant_fields":{"type":"object","description":"Values of the variant by field key."},"lot_fields":{"type":"object","description":"Values of the row's lot by field key (fields whose data level is lot)."},"item_fields":{"type":"object","description":"Values of the row's item passport by field key (fields whose data level is item)."}}},"ImportJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","running","done","failed"],"description":"failed: the job stopped as a whole (see error), not a failed row."},"format":{"type":"string","enum":["json","csv"]},"confirm":{"type":"boolean"},"rule_set_id":{"type":["string","null"],"format":"uuid"},"stats":{"type":"object","properties":{"rows":{"type":"integer"},"processed":{"type":"integer"},"created":{"type":"integer","description":"Rows that created a product."},"updated":{"type":"integer","description":"Rows that found an existing product."},"failed":{"type":"integer"}}},"error":{"type":["object","null"],"properties":{"code":{"type":"string","examples":["key_revoked","key_expired","insufficient_scope","api_not_available","abandoned"]},"message":{"type":"string"}}},"requested_via":{"type":"string","enum":["api","mcp","dashboard","connector"]},"key_prefix":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"}}},"ImportJobDetail":{"allOf":[{"$ref":"#/components/schemas/ImportJob"},{"type":"object","properties":{"errors":{"type":"array","maxItems":1000,"description":"The failed rows in row order (the first 1,000).","items":{"allOf":[{"$ref":"#/components/schemas/Issue"},{"type":"object","properties":{"row":{"type":"integer"}}}]}}}}]},"ImportRow":{"type":"object","properties":{"row":{"type":"integer","minimum":1},"status":{"type":"string","enum":["pending","ok","failed"]},"result":{"type":["object","null"],"properties":{"product_id":{"type":"string","format":"uuid"},"product":{"type":"string","enum":["created","updated","unchanged"]},"variant_id":{"type":"string","format":"uuid"},"variant":{"type":"string","enum":["created","updated","unchanged"]},"passport_id":{"type":"string","format":"uuid"},"passport":{"type":"string","enum":["created","existing"]},"lot_id":{"type":"string","format":"uuid","description":"The lot of the row, when it has one."},"lot":{"type":"string","enum":["created","existing"]},"fields":{"type":"integer"},"variant_fields":{"type":"integer"},"lot_fields":{"type":"integer","description":"Only when the row has lot_fields."},"item_fields":{"type":"integer","description":"Only when the row has item_fields."}}},"errors":{"type":"array","items":{"$ref":"#/components/schemas/Issue"}},"processed_at":{"type":["string","null"],"format":"date-time"}}},"MediaUploadCreate":{"type":"object","required":["content_type","bytes"],"description":"Either purpose (a new record; identification_photo needs product_id; images need alt text) or media_id (the file of an existing pending record). Documents and CSV imports come later.\n","properties":{"purpose":{"type":"string","enum":["identification_photo","logo","page_media"]},"media_id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","image/avif"]},"bytes":{"type":"integer","minimum":1},"filename":{"type":"string","maxLength":200},"alt":{"type":"object","additionalProperties":{"type":"string","maxLength":300},"description":"Alt text per locale."},"licence":{"type":"string","maxLength":200},"source_url":{"type":"string","format":"uri"}}},"MediaUpload":{"type":"object","properties":{"media_id":{"type":"string","format":"uuid"},"upload_url":{"type":"string","format":"uri"},"method":{"type":"string","enum":["PUT"]},"headers":{"type":"object","additionalProperties":{"type":"string"}},"max_bytes":{"type":"integer"},"expires_at":{"type":"string","format":"date-time"}}},"MediaAsset":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["image","logo","document"]},"purpose":{"type":"string","enum":["identification_photo","logo","page_media","document"]},"url":{"type":["string","null"],"format":"uri"},"content_type":{"type":["string","null"]},"bytes":{"type":["integer","null"]},"sha256":{"type":["string","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"alt":{"type":"object","additionalProperties":{"type":"string"}},"licence":{"type":["string","null"]},"source_url":{"type":["string","null"]},"status":{"type":"string","enum":["pending","ready","removed"]},"created_at":{"type":"string","format":"date-time"}}},"Theme":{"type":"object","description":"public.brand_themes (PR 14). The brand colour needs 4.5:1 on white, the accent keeps body text at 7:1; surface is Custom (Enterprise) only.\n","properties":{"primary":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"accent":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"surface":{"type":["string","null"],"pattern":"^#[0-9a-fA-F]{6}$"},"heading_font":{"type":"string","enum":["system-sans","system-serif","ibm-plex-sans","ibm-plex-serif"]},"body_font":{"type":"string","enum":["system-sans","system-serif","ibm-plex-sans","ibm-plex-serif"]},"corners":{"type":"string","enum":["square","rounded"]},"logo_media":{"type":["string","null"],"format":"uuid"},"revision":{"type":"integer","readOnly":true}}},"PageBlock":{"type":"object","required":["id","type"],"description":"The one JSON Schema for blocks is PAGE_DOCUMENT_SCHEMA in packages/shared (dpphost.page/1). Blocks are data, never HTML; texts are objects of one string per language.\n","properties":{"id":{"type":"string","pattern":"^[a-z0-9-]{1,40}$"},"type":{"type":"string","enum":["hero","story","gallery","key_facts","materials_chart","supply_chain","impact_chart","guide","documents","card3d","video","repair","recycle"]},"layout":{"type":"string","enum":["contained","cover"],"description":"hero only: how the block's picture (media) is shown. contained (the default, and what a hero without the member means): whole, inside a padded frame. cover: it fills a 4:3 frame, cropped where its proportions differ; upload 1200 x 900 pixels or more.\n"},"focus":{"type":"object","description":"hero only, read by layout cover: the point of the picture that stays in view when it is cropped, in whole percent of its width from the left edge (x) and of its height from the top edge (y). Default: the centre (50, 50).\n","required":["x","y"],"additionalProperties":false,"properties":{"x":{"type":"integer","minimum":0,"maximum":100},"y":{"type":"integer","minimum":0,"maximum":100}}}},"additionalProperties":true},"BrandPage":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["brand","product"]},"product_id":{"type":["string","null"],"format":"uuid"},"default_locale":{"type":"string"},"locales":{"type":"array","items":{"type":"string"}},"draft_blocks":{"type":"array","items":{"$ref":"#/components/schemas/PageBlock"}},"draft_revision":{"type":"integer","description":"The page's ETag is \"page-{draft_revision}\"."},"draft_changed":{"type":"boolean","description":"The draft differs from the live version (or nothing is live)."},"released_version":{"type":["integer","null"]},"check_again":{"type":["object","null"],"description":"Set when a passport publish made the live version fail its release check (the page stays live); cleared by the next release, rollback or unpublish.\n","properties":{"since":{"type":"string","format":"date-time"},"checked_at":{"type":"string","format":"date-time","description":"When the reasons were last found different."},"version":{"type":"integer","description":"The live page version that was checked."},"passport_id":{"type":"string","format":"uuid"},"passport_version":{"type":"integer","description":"The publish that found it first."},"blocked":{"type":"array","items":{"type":"object"},"description":"As in 422 release_blocked."},"truncated":{"type":"boolean"}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PageVersion":{"type":"object","properties":{"version":{"type":"integer","minimum":1},"released_at":{"type":["string","null"],"format":"date-time"},"released_via":{"$ref":"#/components/schemas/Channel"},"rollback_of":{"type":["integer","null"]},"layer_id":{"type":"string","description":"The resolver's layer snapshot id (docs/api/resolver.md §3.3)."},"tier":{"type":"string","enum":["premium","custom"]},"locales":{"type":"array","items":{"type":"string"}},"live":{"type":"boolean"},"written":{"type":"boolean","description":"The layer snapshot is on the resolver (passports show it)."}}},"ReleaseResult":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["released","approval_required"]},"version":{"$ref":"#/components/schemas/PageVersion"},"approval":{"$ref":"#/components/schemas/Approval"},"layer_id":{"type":"string"},"tier":{"type":"string","enum":["premium","custom"]},"warnings":{"type":"array","items":{"type":"object"}}}},"WebhookEventType":{"type":"string","description":"page.check_again since 20260928100000; the owner events came with plan PR 17; lot.release_published with sparse items S3 (20261009700000); safety_notice.published with owner registration v2 (20261010000000); delivery.sent, delivery.received, stock.missing and owner.returned with bike makers' track B (20261011800000); page.released is still open.","enum":["passport.published","passport.withdrawn","passport.redacted","approval.approved","approval.rejected","import.finished","page.check_again","owner.claimed","owner.transferred","warranty.registered","repair.requested","lot.release_published","safety_notice.published","delivery.sent","delivery.received","stock.missing","owner.returned"]},"WebhookWrite":{"type":"object","additionalProperties":false,"properties":{"url":{"type":"string","format":"uri","maxLength":2000,"pattern":"^https://","description":"https on port 443 with a public DNS name: no IP address, user name, fragment, localhost or internal name."},"events":{"type":"array","minItems":1,"maxItems":20,"uniqueItems":true,"items":{"$ref":"#/components/schemas/WebhookEventType"}},"enabled":{"type":"boolean","default":true},"description":{"type":["string","null"],"maxLength":200}}},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"enabled":{"type":"boolean"},"description":{"type":["string","null"]},"secret_hint":{"type":"string","description":"whsec_… and the last 4 characters of the secret"},"previous_secret_expires_at":{"type":["string","null"],"format":"date-time","description":"Until then deliveries are signed with the old secret too."},"disabled_reason":{"type":["string","null"],"enum":["manual","failing",null]},"disabled_at":{"type":["string","null"],"format":"date-time"},"consecutive_failures":{"type":"integer"},"failing_since":{"type":["string","null"],"format":"date-time"},"last_success_at":{"type":["string","null"],"format":"date-time"},"last_failure_at":{"type":["string","null"],"format":"date-time"},"created_via":{"type":"string","enum":["dashboard","api"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"WebhookWithSecret":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string","pattern":"^whsec_[a-z2-7]{52}$","description":"Shown once (absent from an Idempotency-Key replay). Keep it on your server."}}}]},"WebhookDelivery":{"type":"object","description":"One delivery attempt.","properties":{"id":{"type":"integer"},"delivery_id":{"type":"integer"},"event_id":{"type":"string","format":"uuid","description":"The DPP-Webhook-Id of the delivery."},"event_type":{"$ref":"#/components/schemas/WebhookEventType"},"attempt":{"type":"integer"},"ok":{"type":"boolean"},"status_code":{"type":["integer","null"]},"error":{"type":["string","null"],"description":"Why it failed: the HTTP status, a timeout, a redirect, a refused address ..."},"duration_ms":{"type":["integer","null"]},"delivered_at":{"type":"string","format":"date-time","description":"When the attempt was made."},"delivery_status":{"type":"string","enum":["queued","claimed","delivered","dead"]},"next_attempt_at":{"type":["string","null"],"format":"date-time"}}},"OwnerItemRef":{"type":"object","additionalProperties":false,"required":["gtin","serial"],"properties":{"gtin":{"type":"string","description":"GTIN-8, -12, -13 or -14 of one of the organisation's variants"},"serial":{"type":"string","maxLength":20,"description":"GS1 AI 21 (character set 82)"}}},"ClaimCodeItem":{"type":"object","additionalProperties":false,"required":["gtin","serial"],"properties":{"gtin":{"type":"string","description":"GTIN-8, -12, -13 or -14 of one of the organisation's variants"},"serial":{"type":"string","maxLength":20,"description":"GS1 AI 21 (character set 82)"},"lot":{"type":["string","null"],"maxLength":20,"description":"GS1 AI 10 (character set 82), the batch: recorded on the item and in its claim link"}}},"OwnerItem":{"type":"object","properties":{"gtin":{"type":"string","description":"14 digits"},"serial":{"type":"string"},"resolver_path":{"type":"string","description":"/01/{gtin}/21/{serial}"}}},"ClaimCodeResult":{"type":"object","properties":{"gtin":{"type":"string"},"serial":{"type":"string"},"lot":{"type":["string","null"],"description":"The item's batch (GS1 AI 10)"},"resolver_path":{"type":"string"},"item_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["issued","registered","invalid"]},"code":{"type":"string","description":"issued only: XXXX-XXXX-XXXX-XXXX, shown once (never in an Idempotency-Key replay)"},"claim_url":{"type":"string","format":"uri","description":"issued only: https://app.dpphost.eu/owner/{handle}?item={item path}#code={code}, the registration link for a QR code or an order email; shown once (never in an Idempotency-Key replay)"},"problem":{"type":"string","description":"invalid only"}}},"OwnerRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string"},"kind":{"type":"string","enum":["repair","dispute","guarantee"],"description":"guarantee: a claim under the brand's guarantee (BM-C)"},"status":{"type":"string","enum":["open","in_progress","closed"]},"message":{"type":["string","null"]},"owner_email":{"type":["string","null"]},"item":{"allOf":[{"$ref":"#/components/schemas/OwnerItem"},{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The owner item."},"product":{"type":["string","null"],"description":"The product's title."}}}]},"receipt":{"type":["object","null"],"description":"BM-C: the shop's receipt the owner attached to a repair request or guarantee claim, while the request is open or in progress; else null. Its type and size only: the file is never available through the API or MCP (the brand's editors download it in the dashboard).\n","properties":{"mime_type":{"type":"string","enum":["application/pdf","image/jpeg","image/png"]},"bytes":{"type":"integer","minimum":1}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"closed_at":{"type":["string","null"],"format":"date-time"}}},"HandoverItemInput":{"oneOf":[{"type":"object","additionalProperties":false,"required":["gtin","serial"],"properties":{"gtin":{"type":"string","maxLength":14,"description":"GTIN-8, -12, -13 or -14 of a linked brand's product"},"serial":{"type":"string","maxLength":20,"description":"GS1 AI 21 (character set 82)"},"lot":{"type":["string","null"],"maxLength":20,"description":"GS1 AI 10, the batch: recorded on the item when it has none"}}},{"type":"object","additionalProperties":false,"required":["link"],"properties":{"link":{"type":"string","maxLength":2000,"description":"A GS1 Digital Link URI of any host: /01/{gtin}[/10/{lot}]/21/{serial}"}}}]},"HandoverEmail":{"type":"object","additionalProperties":false,"required":["to"],"properties":{"to":{"type":"string","maxLength":320,"description":"The customer's address; kept only in the queued email"},"locale":{"type":["string","null"],"description":"The email's language (en, de, fr, es, it, nl, pl, da, zh-Hans, tr); English by default"}}},"HandoverCreate":{"type":"object","additionalProperties":false,"required":["items","kind"],"properties":{"brand_org_id":{"type":["string","null"],"format":"uuid","description":"The brand; may be left out when the items name one linked brand"},"items":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/HandoverItemInput"}},"kind":{"type":"string","enum":["live","link"],"description":"live: the counter's QR; link: a one-time link, valid 30 days"},"sold_on":{"type":["string","null"],"format":"date","description":"The sale date (default today; not in the future, not before 2000): the guarantee's purchase date"},"order_ref":{"type":["string","null"],"minLength":1,"maxLength":64,"description":"Shown to the shop only (GET …/handovers?order_ref=)"},"email":{"$ref":"#/components/schemas/HandoverEmail"},"source":{"type":["string","null"],"enum":["counter","dashboard","woocommerce","shopify","api",null],"description":"Ignored: DPP Host records api for a key and the platform for a connection"},"recalls_checked":{"type":["array","null"],"minItems":1,"maxItems":50,"items":{"type":"string","format":"uuid"},"description":"BM-B: the published recalls whose action the staff confirmed done for these items (the notice ids a recall problem names)"},"with_parts":{"type":["boolean","null"],"description":"BM-B: a bike the shop holds takes along the batteries paired with it that the shop holds (default true)"}}},"HandoverReturn":{"type":"object","additionalProperties":false,"properties":{"items":{"type":"array","minItems":1,"maxItems":10,"description":"The items that came back; left out, every item still registered from the handover","items":{"type":"object","additionalProperties":false,"required":["gtin","serial"],"properties":{"gtin":{"type":"string","maxLength":14},"serial":{"type":"string","maxLength":20}}}}}},"HandoverItem":{"type":"object","properties":{"gtin":{"type":"string","description":"14 digits"},"serial":{"type":"string"},"lot":{"type":["string","null"]},"product":{"type":"string"},"variant":{"type":["string","null"]},"photo":{"type":["object","null"],"description":"The product's photo (owner_item_photo)"},"returned_at":{"type":["string","null"],"format":"date-time","description":"BM-B: when it came back (POST …/return); done handovers"}}},"Handover":{"type":"object","description":"A handover as the shop sees it: never an owner, an address or a later registration. state open with kind live carries qr (the QR's link for this 5-second window and the milliseconds until it changes); reserved: a customer scanned it; buyer_waiting: the signed-in customer waits for the staff's yes (buyer: their short name, Anna K.); open with kind link: hint (the code's last four), emailed_at; done: at and to (the short name, null 30 days after the end); cancelled: reason (retailer, removed, unavailable); expired.\n","required":["id","brand","items","sold_on","source","created_at","state"],"properties":{"id":{"type":"string","format":"uuid"},"brand":{"type":"object","properties":{"name":{"type":"string"},"handle":{"type":"string","description":"The brand's owner area"}}},"items":{"type":"array","items":{"$ref":"#/components/schemas/HandoverItem"}},"sold_on":{"type":"string","format":"date"},"order_ref":{"type":["string","null"]},"source":{"type":"string","enum":["counter","dashboard","woocommerce","shopify","api"]},"created_at":{"type":"string","format":"date-time"},"state":{"type":"string","enum":["open","reserved","buyer_waiting","done","cancelled","expired"]},"kind":{"type":"string","enum":["live","link"]},"qr":{"type":"object","properties":{"key":{"type":"string"},"token":{"type":"string"},"window_ms":{"type":"integer"},"url":{"type":"string","format":"uri","description":"https://app.dpphost.eu/owner/{handle}/shop#qr={key}.{token}: draw it as the QR"}}},"hint":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"emailed_at":{"type":["string","null"],"format":"date-time"},"reserved_until":{"type":"string","format":"date-time"},"buyer":{"type":["string","null"],"description":"buyer_waiting: the customer's short name"},"via":{"type":"string","enum":["apple","google","email"],"description":"buyer_waiting: how the customer signed in"},"asked_at":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"},"at":{"type":"string","format":"date-time"},"to":{"type":["string","null"]},"reason":{"type":"string","enum":["retailer","removed","unavailable"]},"recalls_checked":{"type":"array","items":{"type":"string","format":"uuid"},"description":"BM-B: the recalls the staff confirmed done ([] for none)"},"returnable_until":{"type":"string","format":"date-time","description":"BM-B, done: until when POST …/return can undo it"},"receipt":{"type":["object","null"],"description":"BM-C, an open handover only (state open, reserved or buyer_waiting): the receipt the shop added, once its file is stored; null before. A handover that ended has no receipt member.\n","properties":{"mime_type":{"type":"string","enum":["application/pdf","image/jpeg","image/png"]},"bytes":{"type":"integer","minimum":1},"added_at":{"type":"string","format":"date-time"}}},"link_url":{"type":"string","format":"uri","description":"Only in the answer that made a link handover without email: https://app.dpphost.eu/owner/{handle}/shop#code={code}, shown once"}}},"HandoverLink":{"type":"object","properties":{"link_url":{"type":"string","format":"uri","description":"https://app.dpphost.eu/owner/{handle}/shop#code={code}, shown once"},"expires_at":{"type":"string","format":"date-time"}}},"HandoverEligibility":{"type":"object","properties":{"gtin":{"type":"string","description":"As given"},"eligible":{"type":"boolean"},"brand":{"type":["object","null"],"properties":{"name":{"type":"string"}}}}},"ReceiptUploadCreate":{"type":"object","additionalProperties":false,"required":["content_type","bytes"],"properties":{"content_type":{"type":"string","enum":["application/pdf","image/jpeg","image/png"],"description":"The receipt's type"},"bytes":{"type":"integer","minimum":1,"maximum":5242880,"description":"The file's size: the PUT sends at most this many bytes"}}},"ReceiptUpload":{"type":"object","required":["upload_id","upload_url","method","max_bytes","expires_at"],"description":"BM-C: how to send the receipt's file. PUT it to upload_url with exactly headers, a Content-Length and the bytes as the body, once, before expires_at, from the shop's server (never a browser). The receipts Worker answers 201 {receipt_id, bytes, mime_type, removed_metadata}, or a problem {status, code, detail} (§9b).\n","properties":{"upload_id":{"type":"string","format":"uuid"},"upload_url":{"type":"string","format":"uri","description":"https://receipts.dpphost.eu/v1/uploads/{upload_id}"},"method":{"type":"string","enum":["PUT"]},"headers":{"type":"object","description":"Send exactly these with the PUT. Shown once: an Idempotency-Key replay answers without them (begin again).","required":["Authorization","Content-Type"],"properties":{"Authorization":{"type":"string","pattern":"^Bearer [A-Za-z0-9_-]{43}$","description":"The upload's one-time token (15 minutes, single use)"},"Content-Type":{"type":"string","enum":["application/pdf","image/jpeg","image/png"]}}},"max_bytes":{"type":"integer","minimum":1,"description":"The most the PUT may send: the bytes asked for"},"expires_at":{"type":"string","format":"date-time","description":"The PUT works until then (15 minutes)"}}},"BikeKind":{"type":"string","enum":["bicycle","epac"],"description":"bicycle (EN ISO 4210), or epac: an e-bike, an electrically power assisted cycle (EN 15194)"},"BikeBatteryProduct":{"oneOf":[{"type":"object","additionalProperties":false,"required":["id"],"properties":{"id":{"type":"string","format":"uuid","description":"A battery product of the organisation (batteries/draft-1)"}}},{"type":"object","additionalProperties":false,"required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":500,"description":"A new battery product under batteries/draft-1 with the category LMT"}}}]},"BikeBattery":{"type":"object","additionalProperties":false,"required":["name_on","slots"],"description":"An e-bike's battery (leave it out, or null, for a bicycle). own: the brand makes the battery's passport too (product, one battery product; or models, 1 to 4 of {product}). system_maker: the motor system maker publishes it (maker, and model with its gtin when known; or models, 1 to 4 of {model, gtin?}).\n","properties":{"name_on":{"type":"string","enum":["own","system_maker"],"description":"Whose name is on the battery"},"slots":{"type":"integer","minimum":1,"maximum":2,"description":"How many batteries the e-bike carries"},"built_in":{"type":["boolean","null"],"description":"Built in: only a shop replaces them (default false)"},"product":{"$ref":"#/components/schemas/BikeBatteryProduct"},"maker":{"type":"string","minLength":1,"maxLength":200,"description":"The motor system maker's name"},"model":{"type":"string","minLength":1,"maxLength":200,"description":"The battery model's name"},"gtin":{"type":["string","null"],"maxLength":14,"description":"The battery model's GTIN (8 to 14 digits) when known"},"models":{"type":"array","minItems":1,"maxItems":4,"items":{"oneOf":[{"type":"object","additionalProperties":false,"required":["product"],"properties":{"product":{"$ref":"#/components/schemas/BikeBatteryProduct"}}},{"type":"object","additionalProperties":false,"required":["model"],"properties":{"model":{"type":"string","minLength":1,"maxLength":200},"gtin":{"type":["string","null"],"maxLength":14}}}]}}}},"BikeCreate":{"type":"object","additionalProperties":false,"required":["title","kind"],"properties":{"title":{"type":"string","minLength":1,"maxLength":500,"description":"The model and model year"},"kind":{"$ref":"#/components/schemas/BikeKind"},"battery":{"oneOf":[{"$ref":"#/components/schemas/BikeBattery"},{"type":"null"}]},"category":{"type":["string","null"],"minLength":1,"maxLength":200}}},"BikeSetupUpdate":{"type":"object","additionalProperties":false,"required":["kind"],"properties":{"kind":{"$ref":"#/components/schemas/BikeKind"},"battery":{"oneOf":[{"$ref":"#/components/schemas/BikeBattery"},{"type":"null"}]}}},"BikeSetup":{"type":"object","required":["kind","battery"],"properties":{"kind":{"$ref":"#/components/schemas/BikeKind"},"battery":{"type":["object","null"],"description":"An e-bike's battery; null for a bicycle","properties":{"name_on":{"type":"string","enum":["own","system_maker"]},"slots":{"type":"integer"},"built_in":{"type":"boolean"},"maker":{"type":["string","null"],"description":"The motor system maker (system_maker)"},"models":{"type":"array","description":"In the set-up's order: a battery product of the brand {product: {id, title, variants: [{id, title, gtin}]}} or a system maker's model {name, gtin}","items":{"type":"object"}}}}}},"BikeCreated":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"battery_product_id":{"type":["string","null"],"format":"uuid","description":"The first battery product the set-up names (a new one when it gave a title)"},"setup":{"$ref":"#/components/schemas/BikeSetup"}}},"BikeBatchLot":{"type":"object","description":"One size's lot of the batch, or one own battery size's lot of its battery batch","properties":{"variant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":["string","null"]},"gtin":{"type":["string","null"]}}},"product":{"type":"object","description":"Batteries only: the battery product {id, title}"},"lot_id":{"type":"string","format":"uuid"},"bikes":{"type":"integer","description":"Sizes: its bikes (not removed)"},"batteries":{"type":"integer","description":"Battery sizes: its batteries (not removed)"},"upload_status":{"type":["string","null"],"enum":["open","building","failed","ready",null],"description":"The least advanced of its serial lists"},"failed":{"type":["object","null"],"description":"The newest failed list: {upload_id, error, problems, problem_counts}"},"listed_elsewhere":{"type":"boolean","description":"Battery sizes only: the battery side lists the lot (a serial list no factory file made, or an item passport of it); the batch pairs with it and does not publish it"},"release":{"type":"object","properties":{"current":{"type":["integer","null"],"description":"The published release"},"draft":{"type":["integer","null"],"description":"The draft release"},"approval":{"type":["object","null"],"description":"The draft's open publish request {id, status, expires_at}"}}}}},"BikeBatch":{"type":"object","properties":{"batch":{"type":"string","description":"The batch code (GS1 AI 10)"},"made":{"type":["string","null"],"description":"The confirmed date of manufacture of its lots (YYYY-MM)"},"bikes":{"type":"integer"},"in_stock":{"type":"integer","description":"Its bikes the brand holds"},"registered":{"description":"Its bikes registered to an owner: a number from 5, else the text <5","oneOf":[{"type":"integer"},{"type":"string"}]},"status":{"type":"string","enum":["draft","building","ready","published","partly_published"],"description":"draft: no factory file; building: a factory file is open or a serial list is being built; ready: every serial list built, nothing published; published; partly_published.\n"},"failed":{"type":"integer","description":"Serial lists whose build failed"},"sizes":{"type":"array","items":{"$ref":"#/components/schemas/BikeBatchLot"}},"batteries":{"type":"array","items":{"$ref":"#/components/schemas/BikeBatchLot"}},"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["open","closed","discarded"]},"file_name":{"type":["string","null"]},"battery_batch":{"type":["string","null"]},"rows_added":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"closed_at":{"type":["string","null"],"format":"date-time"}}}}}},"BikeBatchCopy":{"type":"object","additionalProperties":false,"required":["to"],"properties":{"to":{"type":"string","minLength":1,"maxLength":20,"description":"The new batch code (GS1 AI 10)"}}},"BikeBatchCopied":{"type":"object","properties":{"batch":{"type":"string"},"lots":{"type":"array","items":{"type":"object","properties":{"variant_id":{"type":"string","format":"uuid"},"lot_id":{"type":"string","format":"uuid"}}}},"suggested":{"type":"integer","description":"The batch values copied as suggestions"}}},"BikeLabelRow":{"type":"object","description":"One bike's label data: the frame sticker, the battery labels and the box label. Never a claim code","properties":{"serial":{"type":"string","description":"The frame number"},"size":{"type":["string","null"]},"colour":{"type":["string","null"],"description":"The size's confirmed colour in the organisation's language"},"gtin":{"type":"string","description":"14 digits"},"url":{"type":"string","format":"uri","description":"The bike's page with its batch: https://id.dpphost.eu/01/{gtin}/10/{batch}/21/{frame}"},"state":{"type":"string","enum":["in_stock","missing","sold","taken_back"]},"batteries":{"type":"array","description":"The brand's own batteries the factory fitted to the bike","items":{"type":"object","properties":{"serial":{"type":"string"},"gtin":{"type":"string"},"url":{"type":"string","format":"uri"},"product":{"type":"string","description":"The battery product's title"}}}},"maker_batteries":{"type":"array","description":"The serial numbers of a system maker's batteries the factory fitted","items":{"type":"object","properties":{"serial":{"type":"string"}}}}}},"BikeCopy":{"type":"object","additionalProperties":false,"required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":500,"description":"The new model year's title"},"with_shops":{"type":["boolean","null"],"description":"Add the copy to the shop links that list the bike: a signed-in admin's change; ignored for an API key"}}},"BikeCopied":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"sizes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":["string","null"]}}}},"suggested":{"type":"integer","description":"The values copied as suggestions"},"shops":{"type":"null","description":"Always null for an API key"}}},"FactoryRow":{"type":"object","additionalProperties":false,"required":["frame","size"],"properties":{"row":{"type":"integer","minimum":1,"maximum":999999999,"description":"The row's line in the factory's file, which problems name (default: its place in rows, from 1); each number once"},"frame":{"type":"string","maxLength":200,"description":"The frame number (GS1 AI 21): 1 to 20 characters of GS1 character set 82, unique per brand"},"size":{"type":"string","maxLength":200,"description":"A size of the bike: its title (case ignored), SKU or GTIN"},"battery":{"type":["string","null"],"maxLength":200,"description":"The serial number of the battery fitted at the factory"},"battery_2":{"type":["string","null"],"maxLength":200,"description":"The second battery's serial number"},"battery_gtin":{"type":["string","null"],"maxLength":200,"description":"The battery model's GTIN, where the e-bike takes more than one"}}},"FactoryProblem":{"type":"object","properties":{"row":{"type":"integer"},"code":{"type":"string","enum":["frame_invalid","frame_twice","frame_taken","unknown_size","size_without_gtin","not_ebike","too_many_batteries","battery_invalid","battery_twice","battery_taken","battery_model_needed","battery_unknown_model"]},"serial":{"type":"string","description":"The frame number the problem is about"},"battery":{"type":"string","description":"The battery serial number the problem is about"},"other_row":{"type":"integer","description":"frame_twice and battery_twice: the other row in the call"}}},"FactoryWarning":{"type":"object","properties":{"row":{"type":"integer"},"code":{"type":"string","enum":["no_battery","battery_listed"]}}},"FactoryFileCheck":{"type":"object","additionalProperties":false,"required":["product_id","batch","rows"],"properties":{"product_id":{"type":"string","format":"uuid"},"batch":{"type":"string","minLength":1,"maxLength":20,"description":"The batch code (GS1 AI 10) of every size"},"battery_batch":{"type":["string","null"],"minLength":1,"maxLength":20,"description":"The batch code of the brand's own batteries (null: the same)"},"rows":{"type":"array","minItems":1,"maxItems":5000,"items":{"$ref":"#/components/schemas/FactoryRow"}}}},"FactoryCheckResult":{"type":"object","properties":{"rows":{"type":"integer"},"ready":{"type":"integer","description":"Rows without a problem"},"with_battery":{"type":"integer","description":"Ready rows with a battery"},"batteries_listed":{"type":"integer","description":"Own batteries of ready rows their batch's lot lists already (paired, not listed again)"},"batteries_to_list":{"type":"integer","description":"Own batteries of ready rows the file would list"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/FactoryProblem"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/FactoryWarning"}}}},"FactoryFileCreate":{"type":"object","additionalProperties":false,"required":["product_id","batch"],"properties":{"product_id":{"type":"string","format":"uuid"},"batch":{"type":"string","minLength":1,"maxLength":20,"description":"The batch code (GS1 AI 10) of every size"},"battery_batch":{"type":["string","null"],"minLength":1,"maxLength":20,"description":"The batch code of the brand's own batteries (null: the same)"},"file_name":{"type":["string","null"],"minLength":1,"maxLength":200,"description":"The factory's file name (shown only)"}}},"FactoryFile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":"string","format":"uuid"},"batch":{"type":"string"},"battery_batch":{"type":["string","null"]},"status":{"type":"string","enum":["open","closed","discarded"]},"source":{"type":"string","enum":["dashboard","api"]},"file_name":{"type":["string","null"]},"rows_seen":{"type":"integer"},"rows_added":{"type":"integer"},"rows_refused":{"type":"integer"},"problems":{"type":"array","description":"The first 1,000","items":{"$ref":"#/components/schemas/FactoryProblem"}},"uploads":{"type":"array","description":"The serial lists the file opened, one per size and own battery size (section 6c)","items":{"type":"object","properties":{"variant_id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["bike","battery"]},"upload_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["open","queued","building","built","ready","failed","deleted"]},"serials":{"type":["integer","null"]},"error":{"type":["string","null"],"description":"Why its build failed"},"problems":{"type":["array","null"],"description":"The first 100 problems of a failed build, {code, serial}","items":{"type":"object"}},"problem_counts":{"type":["object","null"],"description":"The count per problem code"}}}},"created_at":{"type":"string","format":"date-time"},"closed_at":{"type":["string","null"],"format":"date-time"}}},"FactoryRows":{"type":"object","additionalProperties":false,"required":["rows"],"properties":{"rows":{"type":"array","minItems":1,"maxItems":5000,"items":{"$ref":"#/components/schemas/FactoryRow"}}}},"FactoryRowsResult":{"type":"object","properties":{"added":{"type":"integer"},"already":{"type":"integer","description":"Rows whose frame number the file added before"},"refused":{"type":"integer","description":"Rows with a problem"},"batteries_listed":{"type":"integer","description":"Own batteries their batch's lot listed already: paired, not listed again"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/FactoryProblem"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/FactoryWarning"}}}},"StockItem":{"type":"object","description":"A bike or own battery as the organisation sees it, never with an owner, a code or a receipt. missing_reason while it is missing; for the brand, sold {at, via, shop: {name} | null} once a registration sold it.\n","properties":{"id":{"type":"string","format":"uuid"},"brand":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"}}},"variant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":["string","null"]},"gtin":{"type":["string","null"]}}},"serial":{"type":"string","description":"The frame number or the battery's serial number"},"lot":{"type":"string","description":"The batch"},"kind":{"type":"string","enum":["bike","battery"]},"state":{"type":"string","enum":["in_stock","missing","sold","taken_back","removed"]},"since":{"type":"string","format":"date-time"},"url":{"type":"string","format":"uri","description":"The item's page"},"batteries":{"type":"array","description":"A bike's batteries as the factory fitted them: {serial, product: {id, title} | null}","items":{"type":"object"}},"paired_with":{"type":["object","null"],"description":"A battery's bike {id, serial}"},"holder":{"type":["object","null"],"description":"The business that holds it {org_id, name}; null once sold or removed"},"received_from":{"type":["object","null"],"description":"BM-B: who delivered it to the business that holds it {org_id, name}, for that business and the brand"},"notices":{"type":"array","description":"BM-B: the brand's published recalls and warnings that apply to it while in stock or missing","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["warning","recall"]},"headline":{"type":"string"},"state":{"type":"string"}}}},"missing_reason":{"type":"string","enum":["lost","stolen"]},"sold":{"type":"object","description":"The brand only: {at, via, shop: {name} | null}"}}},"StockSummary":{"type":"object","properties":{"holders":{"type":"array","description":"For a brand: per business that holds its products' stock, the brand itself included","items":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"type":["string","null"],"enum":["brand","distributor","shop",null]},"in_stock":{"type":"integer"},"missing":{"type":"integer"}}}},"brands":{"type":"array","description":"For a business: per brand whose stock it holds or received; received and passed_on count deliveries (BM-B)","items":{"type":"object","properties":{"brand":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"received":{"type":"integer"},"in_stock":{"type":"integer"},"passed_on":{"type":"integer"},"missing":{"type":"integer"}}}}}},"StockRemove":{"type":"object","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"object","additionalProperties":false,"description":"{id}, {gtin, serial} or {serial}","properties":{"id":{"type":"string","format":"uuid"},"gtin":{"type":"string","maxLength":14},"serial":{"type":"string","maxLength":20}}}}}},"StockRemoveResult":{"type":"object","properties":{"removed":{"type":"integer"},"items":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"The item's place in items (from 0)"},"status":{"type":"string","enum":["removed","refused"]},"id":{"type":"string","format":"uuid"},"serial":{"type":"string"},"kind":{"type":"string","enum":["bike","battery"]},"lot_id":{"type":"string","format":"uuid"},"listed":{"type":"boolean","description":"The batch's serial list still names it: once the batch is released, mark it gone with the reason listed_in_error (POST /lots/{lot_id}/serial-states)"},"problem":{"type":"string","enum":["unknown","ambiguous","duplicate","not_in_stock","delivered","registered"]}}}}}},"StockItemInput":{"type":"object","additionalProperties":false,"description":"{id}, {gtin, serial}, {serial} or {link}","properties":{"id":{"type":"string","format":"uuid","description":"A stock item's id"},"gtin":{"type":"string","maxLength":14},"serial":{"type":"string","maxLength":20},"link":{"type":"string","maxLength":2000,"description":"A GS1 Digital Link URI or element string, as a scanner reads a box or a frame"}}},"StockItemsInput":{"type":"object","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/StockItemInput"}}}},"StockMarkMissing":{"type":"object","additionalProperties":false,"required":["items","reason"],"properties":{"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/StockItemInput"}},"reason":{"type":"string","enum":["lost","stolen"]},"with_parts":{"type":["boolean","null"],"description":"A bike's paired batteries the business holds are marked with it (default true)"}}},"StockMarkResult":{"type":"object","properties":{"marked":{"type":"integer","description":"POST /stock/missing"},"found":{"type":"integer","description":"POST /stock/found"},"items":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"The item's place in items (from 0)"},"status":{"type":"string","enum":["missing","found","refused"]},"id":{"type":"string","format":"uuid"},"serial":{"type":"string"},"kind":{"type":"string","enum":["bike","battery"]},"problem":{"type":"string","enum":["unknown","ambiguous","duplicate","missing","not_in_stock","registered"]},"parts":{"type":"array","description":"The batteries marked with a bike: [{id, serial}]","items":{"type":"object"}}}}}}},"DeliveryReceiver":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"country":{"type":["string","null"]},"kind":{"type":"string","enum":["brand","distributor","shop"]}}},"DeliveryCreate":{"type":"object","additionalProperties":false,"required":["to_org_id"],"properties":{"brand_org_id":{"type":["string","null"],"format":"uuid","description":"The brand whose products go; the key's own organisation by default"},"to_org_id":{"type":"string","format":"uuid","description":"One of GET /delivery-receivers"},"reference":{"type":["string","null"],"minLength":1,"maxLength":64,"description":"The sender's own reference (a delivery note or order number), never a person's name"},"shipped_on":{"type":["string","null"],"format":"date","description":"The day it left (default today; not in the future, not before 2000)"},"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/StockItemInput"}},"send":{"type":["boolean","null"],"description":"Send it in the same call, all or nothing (needs items)"},"start_holder_only":{"type":["boolean","null"],"description":"The brand's first sent delivery starts the holder rule and must say so"}}},"DeliverySend":{"type":"object","additionalProperties":false,"properties":{"start_holder_only":{"type":["boolean","null"],"description":"The brand's first sent delivery starts the holder rule and must say so"}}},"DeliveryRemove":{"type":"object","additionalProperties":false,"required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"string","format":"uuid"},"description":"Stock item ids, as the delivery's serials list them"}}},"DeliveryItemProblem":{"type":"object","properties":{"index":{"type":"integer","description":"The item's place in items, or in the delivery for a send and a draft (from 0)"},"id":{"type":"string","format":"uuid"},"gtin":{"type":"string"},"serial":{"type":"string"},"code":{"type":"string","enum":["unknown","ambiguous","other_brand","not_in_stock","missing","not_published","duplicate","too_many","moved"]},"message":{"type":"string"}}},"DeliveryAddResult":{"type":"object","properties":{"added":{"type":"integer"},"batteries":{"type":"integer","description":"The batteries riding along with the bikes added in this call"},"items":{"type":"integer","description":"The delivery's products now"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryItemProblem"}}}},"Delivery":{"type":"object","description":"A delivery as its sender, its receiver or the brand sees it; never an owner, a code or a receipt. With its serials in GET /deliveries/{delivery_id} and the answers about a draft; a sender's draft also has problems (what stops it from going now; in the answer of POST /deliveries without send, the items that could not be added).\n","properties":{"id":{"type":"string","format":"uuid"},"brand":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"from":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"to":{"type":"object","properties":{"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"direction":{"type":["string","null"],"enum":["in","out",null],"description":"null for the brand looking on"},"status":{"type":"string","enum":["draft","sent","cancelled","undone"]},"reference":{"type":["string","null"]},"shipped_on":{"type":"string","format":"date"},"source":{"type":"string","enum":["dashboard","scan","csv","api","mcp"]},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"},"undone_at":{"type":["string","null"],"format":"date-time"},"undo_until":{"type":["string","null"],"format":"date-time","description":"For the sender, while it may undo the delivery"},"items":{"type":"integer","description":"Its products: bikes, and batteries sent alone"},"batteries":{"type":"integer","description":"The batteries riding along with its bikes"},"lines":{"type":"array","items":{"type":"object","properties":{"product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"}}},"variant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":["string","null"]},"gtin":{"type":["string","null"]}}},"count":{"type":"integer"},"batteries":{"type":"integer"}}}},"serials":{"type":"array","description":"{id, serial, gtin, lot, kind, product, variant, batteries: [{id, serial, gtin}]} per product, in the order added","items":{"type":"object"}},"problems":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryItemProblem"}}}},"AuditEvent":{"type":"object","properties":{"id":{"type":"integer"},"action":{"type":"string"},"target":{"type":["string","null"]},"channel":{"$ref":"#/components/schemas/Channel"},"actor_user_id":{"type":["string","null"],"format":"uuid"},"api_key_prefix":{"type":["string","null"]},"oauth_client_id":{"type":["string","null"]},"request_id":{"type":["string","null"]},"detail":{"type":["object","null"]},"created_at":{"type":"string","format":"date-time"}}},"ConnectRequestCreate":{"type":"object","additionalProperties":false,"required":["platform","shop_host","redirect_uri","code_challenge"],"properties":{"platform":{"type":"string","enum":["woocommerce","shopify"]},"shop_host":{"type":"string","maxLength":253,"description":"The WordPress site host (or *.myshopify.com), without scheme or port"},"redirect_uri":{"type":"string","format":"uri","maxLength":2000},"code_challenge":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$","description":"base64url SHA-256 of the verifier (PKCE S256)"},"code_challenge_method":{"type":"string","enum":["S256"],"default":"S256"},"state":{"type":"string","pattern":"^[A-Za-z0-9._~-]{1,200}$","description":"Returned with the code"}}},"ConnectRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"expires_at":{"type":"string","format":"date-time"},"consent_url":{"type":"string","format":"uri","description":"Open it top-level: https://app.dpphost.eu/connect-shop?req=<id>"}}},"ConnectTokenRequest":{"type":"object","additionalProperties":false,"required":["request_id","code","code_verifier","redirect_uri"],"properties":{"request_id":{"type":"string","format":"uuid"},"code":{"type":"string","pattern":"^dpp_connh_[a-z2-7]{52}$"},"code_verifier":{"type":"string","pattern":"^[A-Za-z0-9._~-]{43,128}$"},"redirect_uri":{"type":"string","description":"Exactly as registered"}}},"ConnectionCredential":{"type":"object","properties":{"connection_id":{"type":"string","format":"uuid"},"credential":{"type":"string","pattern":"^dpp_conn_[a-z2-7]{8}_[a-z2-7]{52}$","description":"Shown once. Keep it on your server."},"credential_prefix":{"type":"string"},"credential_expires_at":{"type":"string","format":"date-time"},"org_id":{"type":"string","format":"uuid"},"platform":{"type":"string","enum":["woocommerce","shopify"]},"shop_host":{"type":"string"},"reconnect":{"type":"boolean","description":"The organisation already linked this shop; its items are kept"}}},"RotatedConnectionCredential":{"type":"object","properties":{"connection_id":{"type":"string","format":"uuid"},"credential":{"type":"string","pattern":"^dpp_conn_[a-z2-7]{8}_[a-z2-7]{52}$","description":"Shown once. Keep it on your server."},"credential_prefix":{"type":"string"},"credential_expires_at":{"type":"string","format":"date-time"},"previous_valid_until":{"type":["string","null"],"format":"date-time","description":"Until when the old credential works (WooCommerce)"}}},"ConnectionSyncRow":{"type":"object","additionalProperties":false,"description":"One product or size of the shop (docs/product/shop-connectors.md §1.6). null counts as absent. Title, size name, SKU and shop_url are written only when the records are created; a later difference becomes a \"Changed in your shop\" notice, never an update.\n","required":["remote_product_id"],"properties":{"remote_product_id":{"type":"string","pattern":"^[!-~]{1,200}$"},"remote_variant_id":{"type":["string","null"],"pattern":"^[!-~]{1,200}$","description":"The size; absent for a simple product."},"gtin":{"type":["string","null"],"description":"GTIN-8, -12, -13 or -14 with a correct check digit and not restricted. Absent, null or blank (after trimming spaces) means \"no GTIN\": DPP Host then gives the item a DPP Host ID or reports needs_gtin (F11). Send no GTIN only for an item with no barcode candidate at all, and only while Connection.dpphost_ids is true or the feed last reported a dpphost_id for the item. A row has no dpphost_id member."},"title":{"type":["string","null"],"description":"Required unless removed."},"variant_title":{"type":["string","null"]},"sku":{"type":["string","null"]},"shop_url":{"type":["string","null"],"description":"The product page; kept only when it is https."},"fields":{"type":["object","null"],"description":"Suggestions by field key: country_making_up (country_of_origin in the general rule set) and commodity_code; other keys, and values the rule set refuses, are dropped."},"removed":{"type":["boolean","null"],"description":"true: the shop deleted or archived the product (or the size); its items are marked removed."}}},"ConnectionSyncRequest":{"type":"object","additionalProperties":false,"required":["rows"],"properties":{"rows":{"type":"array","minItems":1,"maxItems":1000,"items":{"$ref":"#/components/schemas/ConnectionSyncRow"}}}},"ConnectionSyncJob":{"allOf":[{"$ref":"#/components/schemas/ImportJob"},{"type":"object","properties":{"replay":{"type":"boolean","description":"true: the job of an earlier request with this Idempotency-Key."},"rejected":{"type":"array","maxItems":1000,"description":"The rows that were not queued (the job's stats.rows counts the queued ones).","items":{"$ref":"#/components/schemas/ConnectionSyncRejectedRow"}}}}]},"ConnectionSyncRejectedRow":{"type":"object","properties":{"row":{"type":"integer","minimum":1,"description":"The row's position in the request."},"code":{"type":"string","enum":["not_an_object","invalid_text","unknown_member","wrong_type","required","invalid_remote_id","gtin_invalid","gtin_restricted"]},"member":{"type":["string","null"]},"message":{"type":"string"}}},"Connection":{"type":"object","properties":{"connection_id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"platform":{"type":"string","enum":["woocommerce","shopify"]},"shop_host":{"type":"string"},"create_drafts":{"type":"boolean"},"status":{"type":"string","enum":["ok","degraded","broken"]},"credential_prefix":{"type":"string"},"credential_expires_at":{"type":"string","format":"date-time"},"connected_at":{"type":"string","format":"date-time"},"approved_at":{"type":"string","format":"date-time"},"last_contact_at":{"type":["string","null"],"format":"date-time"},"superseded":{"type":"boolean","description":"The request used the credential a rotation replaced; switch to the new one"},"previous_valid_until":{"type":["string","null"],"format":"date-time"},"approval_expires_at":{"type":["string","null"],"format":"date-time","description":"WooCommerce: an admin reconnects before this"},"dpphost_ids":{"type":"boolean","description":"DPP Host issues DPP Host IDs for new items without a GTIN on this connection: the switch is on and the rule set of new products does not require a GTIN (F11). Read it at least every 24 hours and at every full sync; treat a missing member as false."}}},"ConnectionItemPage":{"type":"object","required":["data","next_cursor","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ConnectionItem"}},"next_cursor":{"type":["string","null"],"description":"Send it as since on the next poll; the same cursor when nothing moved, null only before any item exists"},"has_more":{"type":"boolean","description":"More items moved; ask again at once"}}},"ConnectionItem":{"type":"object","required":["item_id","remote_product_id","gtin14","state","link","changed_at"],"properties":{"item_id":{"type":"string","format":"uuid"},"remote_product_id":{"type":"string"},"remote_variant_id":{"type":["string","null"]},"gtin14":{"type":["string","null"],"pattern":"^[0-9]{14}$","description":"null when the item's state and link follow its DPP Host ID, or when it has no identifier (needs_gtin, not_on_dpphost, draft_limit_reached or budget_exceeded before any ID). An item with an ID reports its added GTIN here once that GTIN's model passport is online anywhere."},"dpphost_id":{"type":"string","pattern":"^[A-HJ-NP-Z][2-9A-HJ-NP-Z]{11}$","description":"Present only when the item holds a DPP Host ID (canonical, 12 characters; the last two are a GS1 check pair). The item keeps it for good while its size exists (F11)."},"state":{"type":"string","enum":["draft","online","offline","live_elsewhere","not_on_dpphost","removed","error"],"description":"draft: a person reviews and publishes it in DPP Host. online: this organisation's model passport is online. live_elsewhere: another organisation publishes it (no draft is made; the manufacturer's passport). offline: the passport is not online (see offline_reason). not_on_dpphost: no passport (drafts are off, or none was made; with the notice gtin_held_elsewhere, another organisation holds the GTIN and has not published it yet). removed: the shop removed the product or size. error: see error_code. An item with a DPP Host ID is online when its model passport /DH/{dpphost_id} is published and written; it is never live_elsewhere through its ID.\n"},"blocked":{"type":["string","null"],"enum":["org_not_verified","gtin_not_claimed","awaiting_approval",null],"description":"draft only: what keeps it from being published; null when it waits for a person. A DPP Host ID passport is only ever org_not_verified or awaiting_approval, never gtin_not_claimed."},"offline_reason":{"type":["string","null"],"enum":["going_online","withheld","withdrawn","taken_down",null],"description":"offline only, for this organisation's passport: going_online (published, the page is being written), withheld (a moderation decision), withdrawn (from the market), taken_down; null when another organisation's passport went offline"},"error_code":{"type":["string","null"],"description":"error only: duplicate_gtin (two items of the shop have this GTIN), draft_limit_reached, budget_exceeded, needs_gtin (no GTIN, and no DPP Host ID can be issued: the switch is off, or the rule set of the item's product requires a GTIN), or the code of a failed row"},"passport_id":{"type":["string","null"],"format":"uuid","description":"This organisation's passport of the item; never another organisation's"},"link":{"type":["string","null"],"format":"uri","description":"https://id.dpphost.eu/DH/{dpphost_id} or https://id.dpphost.eu/01/{gtin14}, only when state is online or live_elsewhere, else null. Never copy it: rebuild https://id.dpphost.eu/01/{gtin14} when gtin14 is a valid GTIN-14, else https://id.dpphost.eu/DH/{dpphost_id} when dpphost_id is a valid DPP Host ID (check pair)."},"page_title":{"type":"string","pattern":"^[a-z][a-z_]{1,39}$","description":"Present only when set, and only when state is online or live_elsewhere: the page title code of the rule set of the page the link opens (battery_information: the information page of a battery without a battery passport). That page is not a digital product passport: label the link with the page's title in your language (\"Battery information\"), never as a passport, and show a code you do not know in words. Absent for a passport."},"notice":{"type":"string","enum":["gtin_added_after_id","gtin_held_elsewhere"],"description":"Present only when set. gtin_added_after_id: the shop sends a GTIN for an item with a DPP Host ID and that GTIN's model passport is not online; the item keeps its ID and its link. gtin_held_elsewhere (state not_on_dpphost): another organisation holds the GTIN (an approved claim) and its model passport is not online yet; no draft is made, and the item becomes live_elsewhere with the link once it is online. Treat an unknown notice as absent."},"added_gtin14":{"type":"string","pattern":"^[0-9]{14}$","description":"Present only when set: the GTIN-14 the shop now sends for an item with a DPP Host ID"},"shop_changes":{"type":["object","null"],"description":"\"Changed in your shop\": the title, variant_title, sku or shop_url the shop now sends differently from the records, with the new values"},"changed_at":{"type":"string","format":"date-time","description":"When the item last moved: its state, passport, notices, DPP Host ID or added GTIN"}}}}}}