{"openapi":"3.1.0","info":{"title":"PackAuth API","version":"0.1.0","summary":"Approve packaging before it is printed, shipped, sold, or changed.","description":"PackAuth governs the packaging lifecycle: manifests, rule packs, counterparty evidence,\nscoped approvals and replayable print releases across every product, market and lifecycle stage.\n\nTwo things are worth knowing before you integrate.\n\n**Coverage is stated, never implied.** A market PackAuth does not cover returns\n`not covered` — it does not return a pass. A check that could not run returns\n`insufficient_input` and blocks, because an unrun rule is not a passed rule. Anything\nelse would be a compliance product that lies by omission.\n\n**This document is generated** from the same registry the API itself is routed from,\nso it cannot describe an endpoint that does not exist or miss one that does.","contact":{"name":"PackAuth","email":"hello@packauth.com","url":"https://packauth.com"},"license":{"name":"Apache-2.0","identifier":"Apache-2.0"}},"servers":[{"url":"https://api.packauth.com","description":"Production"}],"tags":[{"name":"approvals","description":"Sign-off, bounded by the scope the approver actually holds. An approval never widens itself."},{"name":"audit-events","description":"Every governed action PackAuth took, with its actor, the engine decision that permitted it and the outcome. A compliance platform that cannot produce its own audit trail is not one."},{"name":"billing","description":"Plan, usage and meters. Read your consumption before it surprises you."},{"name":"change-events","description":"What changed after an approval was given, and whether the approvals it affects have been re-evaluated. Change is the reason approvals go stale."},{"name":"claims","description":"What the product asserts — organic, high-fibre, halal, recyclable. Every claim carries an evidence obligation before it may be printed."},{"name":"compliance-runs","description":"Running a manifest against the packs its markets require. A run is the unit of evidence."},{"name":"counterparties","description":"Suppliers and converters, and the obligations you have placed on them."},{"name":"coverage","description":"What is checked and what is not, per jurisdiction and duty class. Partial coverage stated plainly beats a clean report that was clean because nothing ran."},{"name":"duty-classes","description":"The classes of labelling duty, and who imposes each one."},{"name":"evidence","description":"What counterparties supply against those obligations — certificates, declarations, test reports."},{"name":"evidence-requests","description":"Outstanding asks against a counterparty, and the single-use token they upload through."},{"name":"findings","description":"What a run found. A blocking finding stops a release until it is resolved or waived by someone holding that authority."},{"name":"formula-specs","description":"The recipe behind the label. Ingredients, declared allergens and structured composition — what allergen cross-referencing and compositional minima are checked against."},{"name":"frameworks","description":"The regulations behind the rules. Every clause here was retrieved from a primary source and hashed, so a finding's citation is checkable rather than asserted."},{"name":"jurisdictions","description":"The markets PackAuth covers, and the ones it does not. A market that is not covered says so rather than passing."},{"name":"manifests","description":"A packaging manifest is one product's artwork, materials and claims at a point in time. Everything downstream refers to one."},{"name":"packaging","description":"What the pack is physically made of, component by component, and who converts each one. Food-contact and recyclability checks read this."},{"name":"packs","description":"Rule packs are the regulations, written as checks. Read-only over the API: they are authored in the repo and versioned there, so a run is reproducible."},{"name":"print-releases","description":"The artefact a printer acts on. It records what was checked, against which pack version, by whom — and can be verified later from its reference alone."},{"name":"product-variants","description":"Sellable variants of a product — SKU, GTIN and pack size. A manifest pins one variant, and the barcode check resolves the GTIN through it."},{"name":"products","description":"The things you sell. Classification happens here, and it decides which rule packs a manifest is measured against."},{"name":"remediations","description":"What was actually done about a finding — the record that separates a fixed problem from a waived one."},{"name":"rules","description":"A single rule, and every requirement and clause it traces to."},{"name":"service","description":"Liveness. Public, and the one call you can make before you have a token."},{"name":"stakeholders","description":"Who may review, approve or supply evidence, and the authority each one actually holds. An approval is bounded by the approver's scope, so this is what makes 'who signed this off' a resolvable answer rather than a string."},{"name":"stripe","description":"Payment webhooks. Called by Stripe, not by you."},{"name":"webhook-endpoints","description":"Outbound webhooks. Register a URL, choose the events you want, verify each delivery with the signing secret you were given once."},{"name":"workspaces","description":"Workspaces group a tenant's products. Every product belongs to exactly one, and an integration starts by listing them."}],"security":[{"bearerAuth":[]}],"paths":{"/health":{"get":{"operationId":"health","summary":"Liveness and whether a decision engine is configured. Public.","tags":["service"],"security":[],"parameters":[],"responses":{"200":{"description":"Liveness and whether a decision engine is configured. Public.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/packs":{"get":{"operationId":"list_packs","summary":"Every pack in the catalogue, each flagged with whether this tenant is entitled to it.","tags":["packs"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[],"responses":{"200":{"description":"Every pack in the catalogue, each flagged with whether this tenant is entitled to it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/packs/{pack_id}":{"get":{"operationId":"get_pack","summary":"One pack, including its rules count and price code.","tags":["packs"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[{"name":"pack_id","in":"path","required":true,"schema":{"type":"string"},"description":"The pack id."}],"responses":{"200":{"description":"One pack, including its rules count and price code.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/jurisdictions":{"get":{"operationId":"list_jurisdictions","summary":"Every market PackAuth can classify, with its coverage status stated honestly.","tags":["jurisdictions"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[],"responses":{"200":{"description":"Every market PackAuth can classify, with its coverage status stated honestly.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/products":{"post":{"operationId":"create_product","summary":"Register a product. Unclassified until classify is called.","tags":["products"],"security":[{"bearerAuth":["products:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Register a product. Unclassified until classify is called.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_products","summary":"This tenant's products.","tags":["products"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"This tenant's products.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/workspaces":{"get":{"operationId":"list_workspaces","summary":"This tenant's workspaces. Every product belongs to one, so this is where an integration starts.","tags":["workspaces"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"This tenant's workspaces. Every product belongs to one, so this is where an integration starts.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/products/{product_id}":{"get":{"operationId":"get_product","summary":"One product with its classification and declared claims.","tags":["products"],"security":[{"bearerAuth":["products:read"]}],"parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"string"},"description":"The product id."}],"responses":{"200":{"description":"One product with its classification and declared claims.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/products/{product_id}/classify":{"post":{"operationId":"classify_product","summary":"Classify a product to a category path and sector. Nothing resolves a pack until this succeeds.","tags":["products"],"security":[{"bearerAuth":["products:write"]}],"parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"string"},"description":"The product id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Classify a product to a category path and sector. Nothing resolves a pack until this succeeds.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests":{"get":{"operationId":"list_manifests","summary":"This tenant's packaging manifests. Filter by workspace_id or product_id.","tags":["manifests"],"security":[{"bearerAuth":["manifests:read"]}],"parameters":[],"responses":{"200":{"description":"This tenant's packaging manifests. Filter by workspace_id or product_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"post":{"operationId":"create_manifest","summary":"Open a packaging manifest against a product. The manifest is the unit a run and an approval attach to.","tags":["manifests"],"security":[{"bearerAuth":["manifests:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Open a packaging manifest against a product. The manifest is the unit a run and an approval attach to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests/{manifest_id}/artwork":{"post":{"operationId":"attach_artwork","summary":"Attach a canonicalised artwork version to a manifest. Extraction happens upstream; PackAuth consumes its output and checks it.","tags":["manifests"],"security":[{"bearerAuth":["manifests:write"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Attach a canonicalised artwork version to a manifest. Extraction happens upstream; PackAuth consumes its output and checks it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_manifest_artwork","summary":"Every artwork version attached to this manifest, newest first.","tags":["manifests"],"security":[{"bearerAuth":["manifests:read"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"responses":{"200":{"description":"Every artwork version attached to this manifest, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests/{manifest_id}":{"get":{"operationId":"get_manifest","summary":"One manifest with its lifecycle state.","tags":["manifests"],"security":[{"bearerAuth":["manifests:read"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"responses":{"200":{"description":"One manifest with its lifecycle state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests/{manifest_id}/markets":{"post":{"operationId":"add_market","summary":"Add a target market. Resolves upward through the jurisdiction hierarchy.","tags":["manifests"],"security":[{"bearerAuth":["manifests:write"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Add a target market. Resolves upward through the jurisdiction hierarchy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests/{manifest_id}/status":{"get":{"operationId":"manifest_status","summary":"The manifest's lifecycle state and what is blocking the next transition.","tags":["manifests"],"security":[{"bearerAuth":["manifests:read"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"responses":{"200":{"description":"The manifest's lifecycle state and what is blocking the next transition.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests/{manifest_id}/matrix":{"get":{"operationId":"manifest_matrix","summary":"Per-market releasability: which markets can print today, and what stops the rest.","tags":["manifests"],"security":[{"bearerAuth":["manifests:read"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"responses":{"200":{"description":"Per-market releasability: which markets can print today, and what stops the rest.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/compliance-runs":{"get":{"operationId":"list_compliance_runs","summary":"Compliance runs, newest first. Filter by manifest_id to see one manifest's history.","tags":["compliance-runs"],"security":[{"bearerAuth":["runs:read"]}],"parameters":[],"responses":{"200":{"description":"Compliance runs, newest first. Filter by manifest_id to see one manifest's history.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry runs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"post":{"operationId":"create_run","summary":"Run every resolved rule against the manifest. Deterministic and replayable.","tags":["compliance-runs"],"security":[{"bearerAuth":["runs:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Run every resolved rule against the manifest. Deterministic and replayable.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry runs:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/compliance-runs/{run_id}":{"get":{"operationId":"get_run","summary":"One run with its replay context.","tags":["compliance-runs"],"security":[{"bearerAuth":["runs:read"]}],"parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string"},"description":"The run id."}],"responses":{"200":{"description":"One run with its replay context.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry runs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/compliance-runs/{run_id}/findings":{"get":{"operationId":"run_findings","summary":"Every finding the run raised, with severity and the rule that raised it.","tags":["compliance-runs"],"security":[{"bearerAuth":["runs:read"]}],"parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string"},"description":"The run id."}],"responses":{"200":{"description":"Every finding the run raised, with severity and the rule that raised it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry runs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/findings":{"get":{"operationId":"list_findings","summary":"Open findings across this tenant.","tags":["findings"],"security":[{"bearerAuth":["findings:read"]}],"parameters":[],"responses":{"200":{"description":"Open findings across this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/finding_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry findings:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/findings/{finding_id}/resolve":{"post":{"operationId":"resolve_finding","summary":"Record a finding as resolved, with who decided and on what basis.","tags":["findings"],"security":[{"bearerAuth":["findings:write"]}],"parameters":[{"name":"finding_id","in":"path","required":true,"schema":{"type":"string"},"description":"The finding id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Record a finding as resolved, with who decided and on what basis.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/finding_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry findings:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/evidence":{"get":{"operationId":"list_evidence","summary":"Evidence supplied against counterparty obligations. Filter by counterparty_id.","tags":["evidence"],"security":[{"bearerAuth":["counterparties:read"]}],"parameters":[],"responses":{"200":{"description":"Evidence supplied against counterparty obligations. Filter by counterparty_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/evidence_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry counterparties:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"post":{"operationId":"create_evidence","summary":"Attach an evidence document to a counterparty or product.","tags":["evidence"],"security":[{"bearerAuth":["evidence:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Attach an evidence document to a counterparty or product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/evidence_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry evidence:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/evidence/{evidence_id}/validate":{"post":{"operationId":"validate_evidence","summary":"Record a human sufficiency decision on an evidence item.","tags":["evidence"],"security":[{"bearerAuth":["evidence:write"]}],"parameters":[{"name":"evidence_id","in":"path","required":true,"schema":{"type":"string"},"description":"The evidence id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Record a human sufficiency decision on an evidence item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/evidence_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry evidence:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/counterparties":{"get":{"operationId":"list_counterparties","summary":"This tenant's counterparties — suppliers, converters and the parties you hold evidence from.","tags":["counterparties"],"security":[{"bearerAuth":["counterparties:read"]}],"parameters":[],"responses":{"200":{"description":"This tenant's counterparties — suppliers, converters and the parties you hold evidence from.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/counterparty_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry counterparties:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"post":{"operationId":"create_counterparty","summary":"Register a supplier, laboratory, certifier, printer or distributor.","tags":["counterparties"],"security":[{"bearerAuth":["counterparties:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Register a supplier, laboratory, certifier, printer or distributor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/counterparty_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry counterparties:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/counterparties/{counterparty_id}":{"get":{"operationId":"get_counterparty","summary":"One counterparty with the evidence its type and risk level require.","tags":["counterparties"],"security":[{"bearerAuth":["counterparties:read"]}],"parameters":[{"name":"counterparty_id","in":"path","required":true,"schema":{"type":"string"},"description":"The counterparty id."}],"responses":{"200":{"description":"One counterparty with the evidence its type and risk level require.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/counterparty_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry counterparties:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/approvals":{"get":{"operationId":"list_approvals","summary":"Approvals raised by this tenant, newest first. Filter by manifest_id, scope or status.","tags":["approvals"],"security":[{"bearerAuth":["approvals:read"]}],"parameters":[],"responses":{"200":{"description":"Approvals raised by this tenant, newest first. Filter by manifest_id, scope or status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/approval_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry approvals:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/approvals/{approval_id}":{"get":{"operationId":"get_approval","summary":"One approval with the exact basis it was taken on: artwork hash, evidence snapshot, pack and dictionary versions, and the decision reference.","tags":["approvals"],"security":[{"bearerAuth":["approvals:read"]}],"parameters":[{"name":"approval_id","in":"path","required":true,"schema":{"type":"string"},"description":"The approval id."}],"responses":{"200":{"description":"One approval with the exact basis it was taken on: artwork hash, evidence snapshot, pack and dictionary versions, and the decision reference.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/approval_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry approvals:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/approvals/request":{"post":{"operationId":"request_approval","summary":"Ask a named approver for a scoped approval.","tags":["approvals"],"security":[{"bearerAuth":["approvals:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Ask a named approver for a scoped approval.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/approval_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry approvals:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/approvals/{approval_id}/approve":{"post":{"operationId":"approve_approval","summary":"Grant an approval. Locks an immutable evidence snapshot.","tags":["approvals"],"security":[{"bearerAuth":["approvals:write"]}],"parameters":[{"name":"approval_id","in":"path","required":true,"schema":{"type":"string"},"description":"The approval id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Grant an approval. Locks an immutable evidence snapshot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/approval_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry approvals:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/approvals/{approval_id}/revoke":{"post":{"operationId":"revoke_approval","summary":"Revoke a live approval. Everything relying on it goes stale.","tags":["approvals"],"security":[{"bearerAuth":["approvals:write"]}],"parameters":[{"name":"approval_id","in":"path","required":true,"schema":{"type":"string"},"description":"The approval id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Revoke a live approval. Everything relying on it goes stale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/approval_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry approvals:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/print-releases":{"get":{"operationId":"list_print_releases","summary":"Print releases issued by this tenant, newest first. Filter by manifest_id.","tags":["print-releases"],"security":[{"bearerAuth":["releases:read"]}],"parameters":[],"responses":{"200":{"description":"Print releases issued by this tenant, newest first. Filter by manifest_id.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry releases:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"post":{"operationId":"create_print_release","summary":"Issue a print release against a live approval and a pinned artwork hash.","tags":["print-releases"],"security":[{"bearerAuth":["releases:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Issue a print release against a live approval and a pinned artwork hash.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry releases:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/print-releases/{release_id}":{"get":{"operationId":"get_release","summary":"One release certificate.","tags":["print-releases"],"security":[{"bearerAuth":["releases:read"]}],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string"},"description":"The release id."}],"responses":{"200":{"description":"One release certificate.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry releases:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/print-releases/{release_id}/verify":{"get":{"operationId":"verify_release","summary":"Verify a release certificate from its id alone. Public — this is the link a printer follows.","tags":["print-releases"],"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string"},"description":"The release id."}],"responses":{"200":{"description":"Verify a release certificate from its id alone. Public — this is the link a printer follows.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/billing/entitlements":{"get":{"operationId":"list_entitlements","summary":"Which packs this tenant currently holds.","tags":["billing"],"security":[{"bearerAuth":["billing:read"]}],"parameters":[],"responses":{"200":{"description":"Which packs this tenant currently holds.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry billing:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/billing/checkout":{"post":{"operationId":"create_checkout","summary":"Open a Stripe checkout for a pack or plan.","tags":["billing"],"security":[{"bearerAuth":["billing:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Open a Stripe checkout for a pack or plan.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry billing:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/stripe/webhook":{"post":{"operationId":"stripe_webhook","summary":"Stripe event intake. Public, signature-verified.","tags":["stripe"],"security":[],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Stripe event intake. Public, signature-verified.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/webhook-endpoints":{"post":{"operationId":"create_webhook_endpoint","summary":"Register a URL to receive events. The signing secret is returned once and stored only as a hash.","tags":["webhook-endpoints"],"security":[{"bearerAuth":["billing:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Register a URL to receive events. The signing secret is returned once and stored only as a hash.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry billing:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_webhook_endpoints","summary":"Registered endpoints, their subscribed event types and delivery status. Never the secret.","tags":["webhook-endpoints"],"security":[{"bearerAuth":["billing:read"]}],"parameters":[],"responses":{"200":{"description":"Registered endpoints, their subscribed event types and delivery status. Never the secret.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry billing:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/formula-specs":{"post":{"operationId":"create_formula_spec","summary":"Record a formula specification for a product — ingredients, declared allergens and the structured composition the compositional rules read.","tags":["formula-specs"],"security":[{"bearerAuth":["products:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Record a formula specification for a product — ingredients, declared allergens and the structured composition the compositional rules read.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_formula_specs","summary":"List formula specifications, most recent first, optionally filtered to one product.","tags":["formula-specs"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"List formula specifications, most recent first, optionally filtered to one product.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/packaging":{"post":{"operationId":"create_packaging","summary":"Record a packaging configuration and its components, each with the material it is made of and the supplier who converts it.","tags":["packaging"],"security":[{"bearerAuth":["products:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Record a packaging configuration and its components, each with the material it is made of and the supplier who converts it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_packaging","summary":"List packaging configurations, optionally filtered to one product.","tags":["packaging"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"List packaging configurations, optionally filtered to one product.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/packaging/{packaging_id}/components":{"get":{"operationId":"list_packaging_components","summary":"List the components of one packaging configuration with their materials and suppliers.","tags":["packaging"],"security":[{"bearerAuth":["products:read"]}],"parameters":[{"name":"packaging_id","in":"path","required":true,"schema":{"type":"string"},"description":"The packaging id."}],"responses":{"200":{"description":"List the components of one packaging configuration with their materials and suppliers.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/claims":{"post":{"operationId":"create_claim","summary":"Declare a marketing or certification claim made for a product, so substantiation can be required against it.","tags":["claims"],"security":[{"bearerAuth":["products:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Declare a marketing or certification claim made for a product, so substantiation can be required against it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_claims","summary":"List declared claims, optionally filtered to one product.","tags":["claims"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"List declared claims, optionally filtered to one product.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/manifests/{manifest_id}/bind":{"post":{"operationId":"bind_manifest","summary":"Bind a formula specification, packaging configuration or variant to a manifest so its rules have something to read.","tags":["manifests"],"security":[{"bearerAuth":["manifests:write"]}],"parameters":[{"name":"manifest_id","in":"path","required":true,"schema":{"type":"string"},"description":"The manifest id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Bind a formula specification, packaging configuration or variant to a manifest so its rules have something to read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/packaging_lifecycle_manifest_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/product-variants":{"post":{"operationId":"create_product_variant","summary":"Record a sellable variant of a product — its SKU, GTIN and pack size, which the barcode and net-quantity checks resolve against.","tags":["product-variants"],"security":[{"bearerAuth":["products:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Record a sellable variant of a product — its SKU, GTIN and pack size, which the barcode and net-quantity checks resolve against.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_product_variants","summary":"List product variants, optionally filtered to one product.","tags":["product-variants"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"List product variants, optionally filtered to one product.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/counterparties/{counterparty_id}/approve":{"post":{"operationId":"approve_counterparty","summary":"Approve a counterparty for a named scope of markets and product classes, once its required evidence is complete.","tags":["counterparties"],"security":[{"bearerAuth":["counterparties:write"]}],"parameters":[{"name":"counterparty_id","in":"path","required":true,"schema":{"type":"string"},"description":"The counterparty id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Approve a counterparty for a named scope of markets and product classes, once its required evidence is complete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/counterparty_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry counterparties:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/billing/usage":{"get":{"operationId":"usage_summary","summary":"What has been metered, what has been billed to Stripe, and what is still waiting — per meter, for a period.","tags":["billing"],"security":[{"bearerAuth":["billing:read"]}],"parameters":[],"responses":{"200":{"description":"What has been metered, what has been billed to Stripe, and what is still waiting — per meter, for a period.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry billing:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/findings/{finding_id}/remediate":{"post":{"operationId":"remediate_finding","summary":"Record a remediation against a finding — what was changed or, for a finding that requires human review, the reviewer's decision and its rationale.","tags":["findings"],"security":[{"bearerAuth":["findings:write"]}],"parameters":[{"name":"finding_id","in":"path","required":true,"schema":{"type":"string"},"description":"The finding id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Record a remediation against a finding — what was changed or, for a finding that requires human review, the reviewer's decision and its rationale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/finding_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry findings:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/stakeholders":{"post":{"operationId":"create_stakeholder","summary":"Register a person or organisation who can review, approve or supply evidence, with the authority scopes their role carries.","tags":["stakeholders"],"security":[{"bearerAuth":["products:write"]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Register a person or organisation who can review, approve or supply evidence, with the authority scopes their role carries.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}},"get":{"operationId":"list_stakeholders","summary":"List the stakeholders registered for this tenant and the authority scopes each one holds.","tags":["stakeholders"],"security":[{"bearerAuth":["products:read"]}],"parameters":[],"responses":{"200":{"description":"List the stakeholders registered for this tenant and the authority scopes each one holds.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry products:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/stakeholders/{stakeholder_id}/authority":{"post":{"operationId":"grant_authority","summary":"Grant or revoke authority scopes for a stakeholder, beyond the defaults their role carries.","tags":["stakeholders"],"security":[{"bearerAuth":["approvals:write"]}],"parameters":[{"name":"stakeholder_id","in":"path","required":true,"schema":{"type":"string"},"description":"The stakeholder id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Grant or revoke authority scopes for a stakeholder, beyond the defaults their role carries.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry approvals:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/audit-events":{"get":{"operationId":"list_audit_events","summary":"The audit trail: every governed action, its actor, its engine decision and its outcome, filterable by object and period.","tags":["audit-events"],"security":[{"bearerAuth":["findings:read"]}],"parameters":[],"responses":{"200":{"description":"The audit trail: every governed action, its actor, its engine decision and its outcome, filterable by object and period.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry findings:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/change-events":{"get":{"operationId":"list_change_events","summary":"Material changes detected against manifests, and whether each one has been applied to the approvals it affects.","tags":["change-events"],"security":[{"bearerAuth":["manifests:read"]}],"parameters":[],"responses":{"200":{"description":"Material changes detected against manifests, and whether each one has been applied to the approvals it affects.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry manifests:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/remediations":{"get":{"operationId":"list_remediations","summary":"Remediations recorded against findings — what was changed or reviewed, by whom, and whether it is complete.","tags":["remediations"],"security":[{"bearerAuth":["findings:read"]}],"parameters":[],"responses":{"200":{"description":"Remediations recorded against findings — what was changed or reviewed, by whom, and whether it is complete.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry findings:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/counterparties/{counterparty_id}/request-evidence":{"post":{"operationId":"request_evidence","summary":"Request the evidence a counterparty still owes, issuing a single-use portal token they can upload against.","tags":["counterparties"],"security":[{"bearerAuth":["evidence:write"]}],"parameters":[{"name":"counterparty_id","in":"path","required":true,"schema":{"type":"string"},"description":"The counterparty id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"Request the evidence a counterparty still owes, issuing a single-use portal token they can upload against.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/counterparty_v1"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry evidence:write. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"409":{"description":"The resource is not in a state where this is allowed — see the code for which state it is in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/evidence-requests":{"get":{"operationId":"list_evidence_requests","summary":"Outstanding evidence requests and what each counterparty still owes.","tags":["evidence-requests"],"security":[{"bearerAuth":["evidence:read"]}],"parameters":[],"responses":{"200":{"description":"Outstanding evidence requests and what each counterparty still owes.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry evidence:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/frameworks":{"get":{"operationId":"list_frameworks","summary":"The regulations PackAuth has ingested — jurisdiction, domain, clause count, and how many of its duties are actually enforced.","tags":["frameworks"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[],"responses":{"200":{"description":"The regulations PackAuth has ingested — jurisdiction, domain, clause count, and how many of its duties are actually enforced.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/frameworks/{framework_id}":{"get":{"operationId":"get_framework","summary":"One regulation: its requirements, which are enforced, which are deferred with a deadline, and which are out of scope with a reason.","tags":["frameworks"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[{"name":"framework_id","in":"path","required":true,"schema":{"type":"string"},"description":"The framework id."}],"responses":{"200":{"description":"One regulation: its requirements, which are enforced, which are deferred with a deadline, and which are out of scope with a reason.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/frameworks/{framework_id}/clauses":{"get":{"operationId":"list_framework_clauses","summary":"The clauses retrieved from a regulation, with the source URL and content hash that make each citation checkable.","tags":["frameworks"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[{"name":"framework_id","in":"path","required":true,"schema":{"type":"string"},"description":"The framework id."}],"responses":{"200":{"description":"The clauses retrieved from a regulation, with the source URL and content hash that make each citation checkable.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/coverage":{"get":{"operationId":"coverage_report","summary":"What PackAuth does and does not check, per jurisdiction and duty class, so partial coverage is legible before a release rather than after.","tags":["coverage"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[],"responses":{"200":{"description":"What PackAuth does and does not check, per jurisdiction and duty class, so partial coverage is legible before a release rather than after.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/frameworks/{framework_id}/clauses/{section}":{"get":{"operationId":"get_clause","summary":"One clause, with its retrieval provenance and every requirement and rule that rests on it. The reverse of a finding: start from the law and see what PackAuth does about it.","tags":["frameworks"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[{"name":"framework_id","in":"path","required":true,"schema":{"type":"string"},"description":"The framework id."},{"name":"section","in":"path","required":true,"schema":{"type":"string"},"description":"The section."}],"responses":{"200":{"description":"One clause, with its retrieval provenance and every requirement and rule that rests on it. The reverse of a finding: start from the law and see what PackAuth does about it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/rules/{rule_id}":{"get":{"operationId":"get_rule","summary":"One rule: what it checks, which pack runs it, and every requirement and clause it traces to. A finding names a rule id and nothing could resolve it.","tags":["rules"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string"},"description":"The rule id."}],"responses":{"200":{"description":"One rule: what it checks, which pack runs it, and every requirement and clause it traces to. A finding names a rule id and nothing could resolve it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"404":{"description":"No such resource within this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}},"/v1/duty-classes":{"get":{"operationId":"list_duty_classes","summary":"Every class of labelling duty, and for each one which jurisdictions impose it, which frameworks declare it, and whether PackAuth enforces it.","tags":["duty-classes"],"security":[{"bearerAuth":["packs:read"]}],"parameters":[],"responses":{"200":{"description":"Every class of labelling duty, and for each one which jurisdictions impose it, which frameworks declare it, and whether PackAuth enforces it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The request was malformed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"401":{"description":"No token, or a token that is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"403":{"description":"The token does not carry packs:read. Scope is checked before anything else happens.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A PackAuth API token, scoped to the operations the holder is entitled to. A token is never widened by the operation it calls: an approval request from a principal without approval authority for that market is refused, not escalated."}},"schemas":{"approval_v1":{"title":"PackAuth Approval","description":"A scoped, evidenced, expiring statement of authority over a packaging manifest. An approval names what it was taken against so it can be replayed and staled.","type":"object","required":["approval_id","manifest_id","tenant_id","scope","jurisdictions","evidence_snapshot_id","approver","status","decision_ref"],"additionalProperties":false,"properties":{"approval_id":{"type":"string","pattern":"^app_[A-Za-z0-9]{2,32}$"},"manifest_id":{"type":"string","pattern":"^man_[A-Za-z0-9]{2,32}$"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"scope":{"type":"string","enum":["approved_for_design_brief","approved_for_regulatory_review","approved_for_print","approved_for_market_release","approved_for_claim_use","approved_for_certification_mark_use","approved_for_supplier_use","approved_for_retailer_submission"]},"jurisdictions":{"type":"array","minItems":1,"items":{"type":"string"},"description":"Markets this approval covers. Partial approval is a first-class outcome."},"excluded_jurisdictions":{"type":"array","items":{"type":"string"},"description":"Markets explicitly NOT covered. Recording the exclusion is what makes a partial approval safe."},"artwork_version_id":{"type":"string","pattern":"^artv_[A-Za-z0-9]{2,32}$"},"artwork_file_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"spec_id":{"type":"string"},"evidence_snapshot_id":{"type":"string","pattern":"^evsnap_[A-Za-z0-9]{2,32}$"},"rule_pack_versions":{"type":"object","description":"Pack id -> version in force at the moment of approval. A pack version bump stales this approval.","additionalProperties":{"type":"string"}},"dictionary_versions":{"type":"object","additionalProperties":{"type":"string"}},"approver":{"type":"object","required":["stakeholder_id","role","authority_scope"],"additionalProperties":false,"properties":{"stakeholder_id":{"type":"string","pattern":"^st_[A-Za-z0-9]{2,32}$"},"role":{"type":"string"},"authority_scope":{"type":"string","description":"The authority scope exercised. Must satisfy the approval scope per packauth:registry:authority-scopes."}}},"decision_ref":{"type":"object","description":"The engine verdict that permitted this approval. PackAuth stores the handle, never the engine's internals.","required":["decision_id","evidence_pack_id","decision"],"additionalProperties":false,"properties":{"decision_id":{"type":"string"},"evidence_pack_id":{"type":"string"},"decision":{"type":"string"},"compilation_seal":{"type":"string"},"reason_codes":{"type":"array","items":{"type":"string"}},"obligations":{"type":"array","items":{"type":"string"}}}},"status":{"type":"string","enum":["requested","approved","rejected","revoked","stale","expired"]},"stale_reason":{"type":"string","enum":["artwork_version_changed","formula_spec_changed","target_market_changed","rule_pack_version_changed","standard_version_changed","claim_changed","packaging_material_changed","supplier_evidence_expired","certification_expired","counterparty_suspended","approver_authority_revoked"]},"rejection_reason":{"type":"string"},"requested_at":{"type":"string","format":"date-time"},"approved_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"revoked_at":{"type":"string","format":"date-time"}}},"compliance_run_v1":{"title":"PackAuth Compliance Run","description":"One deterministic execution of the resolved pack set against one manifest version. Carries everything needed to replay it exactly.","type":"object","required":["run_id","manifest_id","tenant_id","run_type","status","replay_context","started_at"],"additionalProperties":false,"properties":{"run_id":{"type":"string","pattern":"^run_[A-Za-z0-9]{2,32}$"},"manifest_id":{"type":"string","pattern":"^man_[A-Za-z0-9]{2,32}$"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"run_type":{"type":"string","enum":["preflight","full","rerun","revalidation"]},"status":{"type":"string","enum":["queued","running","completed","failed"]},"replay_context":{"type":"object","description":"The complete input pin. Two runs with an identical replay_context MUST produce identical findings.","required":["manifest_version","engine_version","rule_pack_versions","dictionary_versions","evaluated_at"],"additionalProperties":false,"properties":{"manifest_version":{"type":"string"},"artwork_file_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"spec_version":{"type":"string"},"evidence_snapshot_id":{"type":"string"},"evidence_snapshot_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"engine_version":{"type":"string"},"rule_pack_versions":{"type":"object","additionalProperties":{"type":"string"}},"dictionary_versions":{"type":"object","additionalProperties":{"type":"string"}},"extraction_provider":{"type":"string"},"extraction_provider_version":{"type":"string"},"evaluated_at":{"type":"string","format":"date-time","description":"Injected clock. The rule engine never reads the wall clock itself."},"input_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}}},"resolved_packs":{"type":"array","items":{"type":"object","required":["pack_id","version","rules_executed"],"additionalProperties":false,"properties":{"pack_id":{"type":"string"},"version":{"type":"string"},"rules_executed":{"type":"integer","minimum":0},"rules_skipped":{"type":"integer","minimum":0}}}},"unavailable_packs":{"type":"array","description":"Applicable packs that did NOT run, and why. The honest counterpart of resolved_packs.","items":{"type":"object","required":["pack_id","reason"],"additionalProperties":false,"properties":{"pack_id":{"type":"string"},"reason":{"type":"string","enum":["planned","not_entitled"]}}}},"uncovered_markets":{"type":"array","description":"Target markets with no enforced rules. Reported as not covered — never reported as passing.","items":{"type":"string"}},"finding_counts":{"type":"object","additionalProperties":false,"properties":{"blocking":{"type":"integer","minimum":0},"major":{"type":"integer","minimum":0},"minor":{"type":"integer","minimum":0},"advisory":{"type":"integer","minimum":0},"insufficient_input":{"type":"integer","minimum":0}}},"output_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$","description":"Hash of the ordered finding set. A rerun with the same input_hash must reproduce this."},"decision_ref":{"type":"object","additionalProperties":false,"properties":{"decision_id":{"type":"string"},"evidence_pack_id":{"type":"string"},"decision":{"type":"string"}}},"error":{"type":"string"},"started_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time"}}},"counterparty_v1":{"title":"PackAuth Counterparty","description":"An external party in the packaging chain, with the evidence it owes and the scope it is approved for.","type":"object","required":["counterparty_id","tenant_id","type","legal_name","risk_level","status"],"additionalProperties":false,"properties":{"counterparty_id":{"type":"string","pattern":"^cp_[A-Za-z0-9]{2,32}$"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"workspace_id":{"type":"string"},"type":{"type":"string","description":"Resolves against packauth:registry:counterparties."},"legal_name":{"type":"string","minLength":1},"country":{"type":"string"},"markets_supported":{"type":"array","items":{"type":"string"}},"products_supported":{"type":"array","items":{"type":"string"}},"risk_level":{"type":"string","enum":["low","medium","high","critical"]},"required_evidence":{"type":"array","items":{"type":"string"},"description":"Derived from type and risk level, never hand-maintained per counterparty."},"evidence_status":{"type":"object","additionalProperties":false,"properties":{"complete":{"type":"boolean"},"missing":{"type":"array","items":{"type":"string"}},"expired":{"type":"array","items":{"type":"string"}},"expiring_within_30d":{"type":"array","items":{"type":"string"}}}},"approved_scope":{"type":"object","additionalProperties":false,"properties":{"markets":{"type":"array","items":{"type":"string"}},"product_classes":{"type":"array","items":{"type":"string"}},"approved_at":{"type":"string","format":"date-time"},"approved_by":{"type":"string"}}},"status":{"type":"string","enum":["counterparty_created","classified","risk_profile_created","required_documents_generated","evidence_requested","evidence_received","evidence_validated","approved_for_scope","monitored","expired_document_detected","revalidation_required","suspended","offboarded"]},"revalidation_due_at":{"type":"string","format":"date"},"suspension_reason":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"evidence_v1":{"title":"PackAuth Evidence","description":"A document offered in support of a compliance position, with its type, validity window, owning counterparty and the scope it covers.","type":"object","required":["evidence_id","tenant_id","evidence_type","status","file_hash"],"additionalProperties":false,"properties":{"evidence_id":{"type":"string","pattern":"^ev_[A-Za-z0-9]{2,32}$"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"workspace_id":{"type":"string"},"counterparty_id":{"type":"string","pattern":"^cp_[A-Za-z0-9]{2,32}$"},"product_id":{"type":"string"},"evidence_type":{"type":"string","description":"Resolves against packauth:registry:evidence-types."},"version":{"type":"integer","minimum":1},"file_key":{"type":"string","description":"Tenant-prefixed R2 key."},"file_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"valid_from":{"type":"string","format":"date"},"valid_to":{"type":"string","format":"date"},"scope":{"type":"object","description":"What this document actually covers. An in-date certificate that does not cover the destination market does not support the claim.","additionalProperties":false,"properties":{"markets":{"type":"array","items":{"type":"string"}},"product_ids":{"type":"array","items":{"type":"string"}},"sites":{"type":"array","items":{"type":"string"}},"materials":{"type":"array","items":{"type":"string"}}}},"extracted_metadata":{"type":"object","description":"Fields lifted by the extraction gateway. Extraction is a convenience, never a validation.","additionalProperties":true},"status":{"type":"string","enum":["requested","received","extracted","validated","insufficient","expired","superseded","withdrawn"]},"human_validation":{"type":"object","description":"Required for evidence types where requires_human_validation is true. Legal sufficiency is a human call.","required":["stakeholder_id","sufficient","decided_at"],"additionalProperties":false,"properties":{"stakeholder_id":{"type":"string"},"sufficient":{"type":"boolean"},"notes":{"type":"string"},"decided_at":{"type":"string","format":"date-time"}}},"superseded_by":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"finding_v1":{"title":"PackAuth Finding","description":"One deterministic rule outcome against one manifest. A finding always carries the rule that produced it, the market it concerns, what is wrong and what to do about it.","type":"object","required":["finding_id","run_id","manifest_id","tenant_id","rule_id","rule_pack_id","severity","category","issue","recommendation","status","human_review_required"],"additionalProperties":false,"properties":{"finding_id":{"type":"string","pattern":"^find_[A-Za-z0-9]{2,32}$"},"run_id":{"type":"string","pattern":"^run_[A-Za-z0-9]{2,32}$"},"manifest_id":{"type":"string","pattern":"^man_[A-Za-z0-9]{2,32}$"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"rule_id":{"type":"string"},"rule_pack_id":{"type":"string"},"rule_pack_version":{"type":"string"},"severity":{"type":"string","enum":["blocking","major","minor","advisory","insufficient_input"],"description":"insufficient_input means the rule could not run. It blocks — an unrun rule is not a passed rule."},"category":{"type":"string"},"jurisdiction":{"type":"string"},"affected_object_type":{"type":"string","enum":["artwork_zone","artwork_version","formula_spec","packaging_component","evidence","counterparty","approval","manifest","claim"]},"affected_object_id":{"type":"string"},"affected_area":{"type":"object","additionalProperties":false,"properties":{"object_type":{"type":"string"},"zone_id":{"type":"string"},"bbox":{"type":"array","items":{"type":"number"},"minItems":4,"maxItems":4}}},"issue":{"type":"string","minLength":1,"description":"What is wrong, in the user's language."},"recommendation":{"type":"string","minLength":1,"description":"What to do about it. A finding without an actionable recommendation is an incomplete rule."},"evidence_refs":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["open","assigned","in_remediation","resolved","dispositioned","waived","reopened"]},"human_review_required":{"type":"boolean"},"blocks_print_release":{"type":"boolean"},"blocks_market_release":{"type":"boolean"},"assigned_stakeholder_id":{"type":"string","pattern":"^st_[A-Za-z0-9]{2,32}$"},"disposition":{"type":"object","description":"Present only when a major finding was dispositioned by an authorised approver. Blocking findings can never be dispositioned.","required":["stakeholder_id","justification","decided_at"],"additionalProperties":false,"properties":{"stakeholder_id":{"type":"string"},"justification":{"type":"string","minLength":1},"decided_at":{"type":"string","format":"date-time"}}},"created_at":{"type":"string","format":"date-time"},"resolved_at":{"type":"string","format":"date-time"}}},"framework_clauses_v1":{"title":"PackAuth Framework Clauses","description":"A regulation retrieved from a primary source, split into its own clauses, with the proof of retrieval. This is the bottom of the citation chain: a rule cites a requirement, a requirement cites a clause, and a clause was READ from bytes somebody can re-fetch and re-hash. Generated by scripts/ingest-framework.mjs and never hand-edited — an edited clause set cites a document nobody retrieved.","type":"object","required":["framework","framework_name","jurisdiction","domain","authority","edition","citation_form","parser_version","retrievals","clause_count","clauses"],"additionalProperties":false,"properties":{"$schema":{"type":"string"},"framework":{"type":"string","pattern":"^[a-z]{2}-[a-z0-9-]+$","description":"Slug, jurisdiction-prefixed. Matches the directory name under spec/compliance/."},"framework_name":{"type":"string","minLength":8},"jurisdiction":{"type":"string","minLength":2,"maxLength":8},"domain":{"type":"string","minLength":2},"authority":{"type":"string","minLength":3},"edition":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"citation_form":{"type":"string","pattern":"\\{section\\}","description":"How a clause of this instrument is cited. Must interpolate {section}, or citations cannot be rendered."},"parser_version":{"type":"string","minLength":3},"retrievals":{"type":"array","minItems":1,"description":"What was fetched, and the hash of exactly what was parsed. Without this the clauses are an assertion.","items":{"type":"object","required":["url","label","bytes","sha256"],"additionalProperties":false,"properties":{"url":{"type":"string","pattern":"^https://"},"label":{"type":"string","minLength":2},"bytes":{"type":"integer","minimum":1},"sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"uk_substitutions":{"type":"array","items":{"type":"string"},"description":"UK authorities substituted into the text. Evidence that a legislation.gov.uk EU instrument is operative in UK law rather than an IP-completion-day archive — the signal that caught uk-organic and uk-medical-devices pointing at law Great Britain does not apply."},"post_2020_revisions":{"type":"integer","minimum":0,"description":"Provisions with post-2020 commencement dates. An archived snapshot has none."},"celex":{"type":"string","pattern":"^[0-9][0-9]{4}[A-Z][0-9]{4}$","description":"The base CELEX of the act, before consolidation."},"consolidated_celex":{"type":"string","description":"The consolidated identifier actually retrieved, e.g. 02011R1169-20250401. Equal to the base CELEX when the act has never been consolidated."},"consolidation_date":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"LATEST_CONSLEG_DATE from the EUR-Lex web service. Null where the act has never been consolidated, which is a legitimate state stated rather than assumed."},"in_force":{"type":["boolean","null"],"description":"RESOURCE_LEGAL_IN-FORCE from the EUR-Lex web service. A repealed act must never sit behind a rule that blocks a print release — this is the flag that stopped Directive 93/42/EEC being ingested to fill the UK Annex I gap."},"known_consolidations":{"type":"array","items":{"type":"string"},"description":"Every consolidation CELLAR's branch notice lists. Recorded so a newer one appearing is a detectable staleness signal, and so the two official sources can be checked against each other."}}}},"clause_count":{"type":"integer","minimum":1},"notes":{"type":"array","items":{"type":"string"}},"clauses":{"type":"array","minItems":1,"items":{"type":"object","required":["section","part","heading","first_paragraph","paragraph_count"],"additionalProperties":false,"properties":{"section":{"type":"string","minLength":1},"part":{"type":"string","minLength":1},"heading":{"type":"string","minLength":1},"first_paragraph":{"type":"string","maxLength":600,"description":"An excerpt for orientation, NOT the requirement. The requirement is the cited section in full at the retrieval URL."},"paragraph_count":{"type":"integer","minimum":0}}}}}},"framework_requirements_v1":{"title":"PackAuth Framework Requirements","description":"The duties a regulation imposes, each bound to the clause it comes from, the authority scope that signs it off, and an honest statement of whether PackAuth enforces it. Hand-authored: this is the reading a person does, and it is the only place in the system where a duty is declared. The shape was held together by one gate and consistency across twelve frameworks before this schema existed.","type":"object","required":["framework","framework_name","framework_version","jurisdiction","sectors","authority","notes","requirements"],"additionalProperties":false,"properties":{"$schema":{"type":"string"},"framework":{"type":"string","pattern":"^[a-z]{2}-[a-z0-9-]+$"},"framework_name":{"type":"string","minLength":8},"framework_version":{"type":"string","minLength":4},"jurisdiction":{"type":"string","minLength":2,"maxLength":8},"sectors":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string"},"description":"The sectors this regulation governs, resolved against packauth:registry:sectors. A regulation is bounded by subject matter as well as by territory — without this, the citation resolver had only the jurisdiction to go on and put food law on a cosmetics finding."},"authority":{"type":"string","minLength":3},"notes":{"type":"array","minItems":1,"items":{"type":"string","minLength":40},"description":"Why this mapping is shaped the way it is. Required, because the reasoning behind a scope decision is the part a reviewer needs and the part that is lost first."},"requirements":{"type":"array","minItems":1,"items":{"type":"object","required":["requirement_id","clause","citation","title","duty_class","level","packauth_enforcement","coverage_status","notes"],"additionalProperties":false,"properties":{"requirement_id":{"type":"string","pattern":"^[a-z]{2}-[a-z0-9-]+\\.[a-z0-9-]+$","description":"<framework>.<duty>. The framework prefix is what lets a rule's back-reference be checked against the framework claiming it."},"clause":{"type":"string","minLength":1},"citation":{"type":"string","minLength":6},"title":{"type":"string","minLength":8},"duty_class":{"type":"string","description":"Resolved against packauth:dictionary:duty-classes. Coverage is reported per duty, not per market — a market with one language rule is not a covered market."},"level":{"type":"string","description":"Resolved against packauth:dictionary:requirement-levels."},"packauth_enforcement":{"type":"object","required":["authority_scope","rules","packs"],"additionalProperties":false,"properties":{"authority_scope":{"type":"string","description":"Resolved against packauth:registry:authority-scopes. A duty with no approver is a check with nobody accountable for its outcome."},"rules":{"type":"array","items":{"type":"string"},"uniqueItems":true},"packs":{"type":"array","items":{"type":"string"},"uniqueItems":true}}},"coverage_status":{"type":"string","description":"Resolved against packauth:dictionary:coverage-status. The honesty bar: coverage is never inflated to look complete, and never understated either."},"decay_deadline":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Required when coverage_status is `designed`. A deferred duty with no date is a duty nobody comes back to."},"notes":{"type":"string","minLength":80,"description":"What PackAuth checks and — the half that matters — what it does NOT. A requirement whose scope note only says what is covered will be read as covering the rest."}},"allOf":[{"if":{"properties":{"coverage_status":{"const":"designed"}},"required":["coverage_status"]},"then":{"required":["decay_deadline"]}},{"if":{"properties":{"coverage_status":{"const":"enforced"}},"required":["coverage_status"]},"then":{"properties":{"packauth_enforcement":{"properties":{"rules":{"minItems":1}}}}}}]}}}},"packaging_lifecycle_manifest_v1":{"title":"PackAuth Packaging Lifecycle Manifest","description":"The single object that carries a packaging item through its lifecycle. Every compliance run, approval and release resolves against exactly one manifest version.","type":"object","required":["manifest_type","manifest_version","manifest_id","tenant_id","workspace_id","product","target_markets","status"],"additionalProperties":false,"properties":{"manifest_type":{"const":"packaging_lifecycle_manifest"},"manifest_version":{"type":"string","pattern":"^\\d+\\.\\d+$"},"manifest_id":{"$ref":"#/$defs/id_man"},"tenant_id":{"$ref":"#/$defs/id_tn"},"workspace_id":{"$ref":"#/$defs/id_ws"},"product":{"type":"object","required":["product_id","name","category","sector"],"additionalProperties":false,"properties":{"product_id":{"type":"string","pattern":"^prod_[A-Za-z0-9]{4,32}$"},"name":{"type":"string","minLength":1},"brand":{"type":"string"},"category":{"type":"string","pattern":"^[a-z_]+(\\.[a-z0-9_]+)*$","description":"Dot-notation category path resolving against packauth:registry:sectors."},"sector":{"type":"string","pattern":"^[a-z_]+$"}}},"variant":{"type":"object","additionalProperties":false,"properties":{"variant_id":{"type":"string","pattern":"^var_[A-Za-z0-9]{4,32}$"},"sku":{"type":"string"},"gtin":{"type":"string","pattern":"^\\d{8}$|^\\d{12,14}$"},"size":{"type":"string"},"unit":{"type":"string"}}},"packaging":{"type":"object","additionalProperties":false,"properties":{"packaging_id":{"type":"string","pattern":"^pack_[A-Za-z0-9]{4,32}$"},"format":{"type":"string"},"components":{"type":"array","items":{"type":"object","required":["component_id","type","material"],"additionalProperties":false,"properties":{"component_id":{"type":"string"},"type":{"type":"string"},"material":{"type":"string","description":"Resolves against packauth:dictionary:materials."},"supplier_counterparty_id":{"$ref":"#/$defs/id_cp"},"food_contact":{"type":"boolean"}}}}}},"artwork":{"type":"object","required":["artwork_version_id","version","file_hash"],"additionalProperties":false,"properties":{"artwork_version_id":{"type":"string","pattern":"^artv_[A-Za-z0-9]{4,32}$"},"version":{"type":"string"},"file_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$","description":"Content hash of the artwork original. This is what an approval pins."},"extracted":{"type":"object","description":"Canonicalised extraction output. Produced before rules run; rules read only from here.","additionalProperties":true,"properties":{"ingredients_text":{"type":"string"},"legal_product_name":{"type":"string"},"detected_claims":{"type":"array","items":{"type":"string"}},"detected_symbols":{"type":"array","items":{"type":"string"}},"detected_elements":{"type":"array","items":{"type":"string"}},"languages_detected":{"type":"array","items":{"type":"string"}},"min_x_height_mm":{"type":"number","minimum":0},"zones":{"type":"array","items":{"type":"object","required":["zone_id"],"additionalProperties":true,"properties":{"zone_id":{"type":"string"},"text":{"type":"string"},"bbox":{"type":"array","items":{"type":"number"},"minItems":4,"maxItems":4}}}},"extraction_provider":{"type":"string"},"extraction_provider_version":{"type":"string"}}}}},"spec_id":{"type":"string","pattern":"^spec_[A-Za-z0-9]{4,32}$"},"formula":{"type":"object","additionalProperties":false,"properties":{"spec_id":{"type":"string"},"version":{"type":"string"},"ingredients":{"type":"array","items":{"type":"string"}},"allergens":{"type":"array","items":{"type":"string"},"description":"Canonical allergen terms from packauth:dictionary:allergens. This is the formula of record."},"structured_spec":{"type":"object","additionalProperties":true}}},"target_markets":{"type":"array","minItems":1,"items":{"type":"object","required":["jurisdiction"],"additionalProperties":false,"properties":{"jurisdiction":{"type":"string","description":"Jurisdiction code from packauth:registry:jurisdictions."},"sales_channel":{"type":"string"},"status":{"type":"string","enum":["planned","active","withdrawn"]}}}},"claims":{"type":"array","items":{"type":"object","required":["claim_text","claim_type"],"additionalProperties":false,"properties":{"claim_id":{"type":"string"},"claim_text":{"type":"string"},"claim_type":{"type":"string","enum":["certification","dietary","nutrition","health","environmental","ethical","efficacy","origin","marketing"]},"detected_from":{"type":"string","enum":["artwork","declared","both"]},"risk_level":{"type":"string","enum":["low","medium","high"]}}}},"required_packs":{"type":"array","items":{"type":"string"},"description":"Pack ids resolved by the Pack Resolver. Enforced packs only."},"unavailable_packs":{"type":"array","description":"Packs that WOULD apply but are planned or unentitled. Present so coverage is never silently overstated.","items":{"type":"object","required":["pack_id","reason"],"additionalProperties":false,"properties":{"pack_id":{"type":"string"},"reason":{"type":"string","enum":["planned","not_entitled"]}}}},"evidence_snapshot_id":{"type":"string","pattern":"^evsnap_[A-Za-z0-9]{4,32}$"},"stage":{"type":"string"},"status":{"type":"string","description":"Current lifecycle state. Must be a state id from packauth:registry:lifecycle-states.","enum":["draft","classified","requirements_generated","evidence_requested","evidence_pending","evidence_complete","artwork_pending","artwork_uploaded","extraction_complete","compliance_running","findings_open","remediation_required","review_required","approved_for_scope","approved_for_print","released_to_print","printed","market_release_pending","released_to_market","live","stale","revalidation_required","superseded","archived"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"$defs":{"id_tn":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"id_ws":{"type":"string","pattern":"^ws_[A-Za-z0-9]{2,32}$"},"id_man":{"type":"string","pattern":"^man_[A-Za-z0-9]{2,32}$"},"id_cp":{"type":"string","pattern":"^cp_[A-Za-z0-9]{2,32}$"}}},"registry_v1":{"title":"PackAuth Registry","description":"The shape every canonical registry under spec/registries/ conforms to. A registry declares the ONE canonical form of a concept; anything referencing that concept resolves here rather than restating it.","type":"object","required":["registry_id","registry_version","concept","constitution_ref"],"properties":{"$schema":{"type":"string"},"registry_id":{"type":"string","pattern":"^packauth:registry:[a-z0-9-]+$","description":"Canonical URN. Stable — a rename is a migration, never a relabel."},"registry_version":{"type":"string","pattern":"^\\d+\\.\\d+\\.\\d+$"},"concept":{"type":"string","pattern":"^[a-z0-9_]+$","description":"The single concept this registry is canonical for. Two registries declaring the same concept is a constitutional violation."},"constitution_ref":{"type":"string","description":"The chapter that governs this registry."},"notes":{"type":"array","items":{"type":"string"},"description":"Why the registry is shaped as it is, and what a reader must not do with it."}}},"release_certificate_v1":{"title":"PackAuth Release Certificate","description":"The artefact a printer or distributor can verify: this artwork hash, approved by this authority, for these markets, on this basis, at this time.","type":"object","required":["release_id","manifest_id","tenant_id","release_type","approval_ids","scope","artwork_file_hash","certificate_hash","status","issued_at"],"additionalProperties":false,"properties":{"release_id":{"type":"string","pattern":"^rel_[A-Za-z0-9]{2,32}$"},"manifest_id":{"type":"string","pattern":"^man_[A-Za-z0-9]{2,32}$"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"release_type":{"type":"string","enum":["print_release","market_release"]},"approval_ids":{"type":"array","minItems":1,"items":{"type":"string"}},"scope":{"type":"object","required":["jurisdictions"],"additionalProperties":false,"properties":{"jurisdictions":{"type":"array","minItems":1,"items":{"type":"string"}},"excluded_jurisdictions":{"type":"array","items":{"type":"string"}},"sales_channels":{"type":"array","items":{"type":"string"}},"variant_skus":{"type":"array","items":{"type":"string"}},"quantity_limit":{"type":"integer","minimum":1}}},"artwork_version_id":{"type":"string"},"artwork_file_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$","description":"The exact artefact released. A printer verifies its file against this."},"evidence_snapshot_id":{"type":"string"},"rule_pack_versions":{"type":"object","additionalProperties":{"type":"string"}},"rail_result":{"type":"object","description":"The rail run that permitted issuance, check by check.","required":["rail_id","checks"],"additionalProperties":false,"properties":{"rail_id":{"type":"string"},"checks":{"type":"array","items":{"type":"object","required":["check","passed"],"additionalProperties":false,"properties":{"check":{"type":"string"},"passed":{"type":"boolean"},"detail":{"type":"string"}}}}}},"decision_ref":{"type":"object","required":["decision_id","evidence_pack_id"],"additionalProperties":false,"properties":{"decision_id":{"type":"string"},"evidence_pack_id":{"type":"string"},"decision":{"type":"string"},"compilation_seal":{"type":"string"}}},"certificate_file_key":{"type":"string","description":"Tenant-prefixed R2 key of the rendered certificate."},"certificate_hash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"verify_url":{"type":"string","format":"uri"},"issued_to":{"type":"object","additionalProperties":false,"properties":{"counterparty_id":{"type":"string"},"legal_name":{"type":"string"},"role":{"type":"string"}}},"status":{"type":"string","enum":["issued","confirmed","superseded","revoked"]},"issued_at":{"type":"string","format":"date-time"},"confirmed_at":{"type":"string","format":"date-time"},"revoked_at":{"type":"string","format":"date-time"},"revocation_reason":{"type":"string"}}},"usage_event_v1":{"title":"PackAuth Usage Event","description":"A billable action reported to a Stripe billing meter. The idempotency key is derived from the work that produced it, so a retry never double-bills.","type":"object","required":["usage_event_id","tenant_id","meter_key","quantity","idempotency_key","created_at"],"additionalProperties":false,"properties":{"usage_event_id":{"type":"string"},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"stripe_customer_id":{"type":"string"},"meter_key":{"type":"string","enum":["packauth_pack_run","packauth_sku_market_check","packauth_ocr_page","packauth_ai_extraction_unit","packauth_api_call","packauth_storage_gb_month","packauth_expert_review_request","packauth_live_monitored_product"]},"quantity":{"type":"number","minimum":0},"idempotency_key":{"type":"string","minLength":1,"description":"Derived deterministically from the producing work, e.g. `${run_id}:${pack_id}`. Never random."},"reported_to_stripe":{"type":"boolean"},"metadata":{"type":"object","additionalProperties":true},"created_at":{"type":"string","format":"date-time"}}},"webhook_event_v1":{"title":"PackAuth Webhook Event","description":"The envelope delivered to a tenant webhook endpoint. Signed with HMAC-SHA256 over `${timestamp}.${body}`.","type":"object","required":["event_id","event_type","tenant_id","object_type","object_id","occurred_at","data"],"additionalProperties":false,"properties":{"event_id":{"type":"string","pattern":"^evt_[A-Za-z0-9]{2,64}$"},"event_type":{"type":"string","description":"Resolves against packauth:registry:events where outbound is true."},"tenant_id":{"type":"string","pattern":"^tn_[A-Za-z0-9]{2,32}$"},"workspace_id":{"type":"string"},"object_type":{"type":"string"},"object_id":{"type":"string"},"occurred_at":{"type":"string","format":"date-time"},"api_version":{"type":"string","const":"v1"},"attempt":{"type":"integer","minimum":1},"data":{"type":"object","additionalProperties":true}}},"error":{"type":"object","description":"Every failure has this shape. The request_id is the part that matters: it is what makes a support conversation about one specific call possible.","required":["error","request_id"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"A stable machine-readable code. Branch on this, never on the message."},"message":{"type":"string","description":"Human-readable. May change; the code will not."}}},"request_id":{"type":"string","description":"Quote this when reporting a problem."}}}}},"x-scopes":{"approvals:read":"Read approvals and their recorded basis — scope, jurisdictions, artwork hash, evidence snapshot, decision. Separate from approvals:write because seeing who approved what is a different authority from approving.","approvals:write":"Grant or withhold an approval within the scope the principal actually holds. Never widens that scope.","billing:read":"Read plan, usage and meter state.","billing:write":"Change plan or manage billing configuration.","counterparties:read":"Read counterparties and the evidence they have supplied.","counterparties:write":"Invite counterparties and manage their obligations.","evidence:read":"Read evidence on file and the outstanding requests against counterparties. Separate from evidence:write because reading what a supplier owes is routine and uploading on their behalf is not.","evidence:write":"Submit evidence against a counterparty obligation.","findings:read":"Read findings raised by a run.","findings:write":"Waive or annotate a finding. A blocking finding can only be waived by a principal holding the approval authority for that market.","manifests:read":"Read packaging manifests and their per-market matrices.","manifests:write":"Create and amend manifests. A manifest in an approved state cannot be amended without superseding it.","packs:read":"Read rule packs and the rules inside them. Read-only: packs are authored in the repo, never through the API.","products:read":"Read this tenant's products and their classification.","products:write":"Create and amend products. Amending a classified product re-opens the checks that depended on the classification.","releases:read":"Read print releases and verify one from its public reference.","releases:write":"Cut a print release. Refused unless every blocking finding for the target markets is resolved or waived.","runs:read":"Read compliance runs and their results.","runs:write":"Start a compliance run against a manifest."}}