{"openapi":"3.1.0","info":{"title":"Palletizer OS API","version":"1.0.0","description":"Plans, plan history and exports from engine 0.5.0. Every plan is a PlanArtifact — the same document, and the same content hash, that `palletizer optimize --json` prints. Nothing in this API authorizes robot motion; the safety gate in the open-source engine is the only thing that does. Limits per plan are in ENTITLEMENTS.md.","license":{"name":"Apache-2.0"}},"servers":[{"url":"/"}],"security":[{"apiKey":[]},{"session":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key (plz_…) from /api/v1/keys. Pro, Cell and Pilot plans."},"session":{"type":"apiKey","in":"cookie","name":"sb-access-token","description":"A signed-in session on the site."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated","invalid_api_key","entitlement_required","session_required","quota_exceeded","rate_limited","invalid_request","not_found","refused","unavailable"]},"message":{"type":"string"},"entitlement":{"type":"string"},"tier":{"type":"string"},"upgrade":{"type":"string","description":"Where the plans are listed."},"upgrade_to":{"type":"string","enum":["pro","cell"],"description":"The cheapest plan that covers exactly what this request asked for. Absent when no plan sold does."},"upgrade_to_name":{"type":"string"},"problems":{"type":"array","items":{"type":"string"}},"retry_after_s":{"type":"integer"}}}}},"Box":{"type":"object","required":["sku_id","length_mm","width_mm","height_mm","weight_kg"],"properties":{"sku_id":{"type":"string","maxLength":64},"length_mm":{"type":"number","exclusiveMinimum":0,"maximum":5000},"width_mm":{"type":"number","exclusiveMinimum":0,"maximum":5000},"height_mm":{"type":"number","exclusiveMinimum":0,"maximum":5000},"weight_kg":{"type":"number","exclusiveMinimum":0,"maximum":2000},"fragility":{"type":"number","minimum":0,"maximum":1}}},"PlanArtifact":{"type":"object","description":"The document `palletizer optimize --json` prints. `palletizer validate` and /plan verify it.","required":["provenance","pallet","placements","metrics","validation","content_hash","plan_id"],"properties":{"content_hash":{"type":"string","pattern":"^[0-9a-f]{64}$"},"plan_id":{"type":"string","pattern":"^plan_[0-9a-f]{16}$"}},"additionalProperties":true}}},"paths":{"/api/v1/me":{"get":{"summary":"Your tier, its entitlements and today's usage","responses":{"200":{"description":"The caller"},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/plans":{"post":{"summary":"Plan an order; saved to history when your plan includes it","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["skus"],"properties":{"skus":{"type":"array","items":{"$ref":"#/components/schemas/Box"}},"pallet":{"type":"object"},"constraints":{"type":"object"},"dataset":{"type":"string","maxLength":200},"dataset_is_synthetic":{"type":"boolean","default":false},"save":{"type":"boolean","default":true},"pallets":{"description":"\"auto\": split the order across as many pallets as it needs, up to your plan's max_pallets_per_request; the response is { artifacts, unplaced, pallet_lower_bound }","oneOf":[{"type":"string","enum":["auto"]},{"type":"integer","enum":[1]}]}}}}}},"responses":{"201":{"description":"The PlanArtifact","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"$ref":"#/components/schemas/PlanArtifact"},"saved":{"type":"boolean"}}}}}},"400":{"description":"The cases, pallet or constraints could not be read. `error.problems` lists each one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"Your plan history, newest first","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Summaries"},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/plans/{hash}":{"get":{"summary":"One plan from your history","parameters":[{"name":"hash","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}}],"responses":{"200":{"description":"The PlanArtifact"},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not in your history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/exports":{"post":{"summary":"URScript pose list or placement CSV, byte-identical to `palletizer export`","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["format"],"properties":{"format":{"type":"string","enum":["urscript","csv"]},"plan":{"$ref":"#/components/schemas/PlanArtifact"},"content_hash":{"type":"string","pattern":"^[0-9a-f]{64}$"}}}}}},"responses":{"200":{"description":"The file","content":{"text/plain":{},"text/csv":{}}},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The plan does not match its hash, or its geometry does not support its verdict, or it is not admissible.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/attestations":{"post":{"summary":"Re-verify a plan and return a signed statement of the verdict (Ed25519)","description":"The statement names the plan hash the service computed, the verdict (VERIFIED, REJECTED, TAMPERED or FORGED), the checks that did not run, and the engine and validator versions. It is signed over its canonical JSON. Verify offline with `palletizer attest verify`. Not a safety certificate; it authorizes no motion.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["plan"],"properties":{"plan":{"$ref":"#/components/schemas/PlanArtifact"}}}}}},"responses":{"201":{"description":"The signed envelope: { schema, alg, key_id, statement, signature }"},"400":{"description":"No plan, or more placements than your plan allows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a plan document this engine can read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/proof-packs":{"post":{"summary":"The plan, its exports and a signed statement, as one reproducible .zip","description":"Contains plan.json, placements.csv, program.urscript.txt, attestation.json and a README that says how to check each of them offline with the open-source CLI. Needs both export formats and a signature, so it needs both entitlements, and it spends one attestation from the daily count. The bytes are deterministic: the same plan and statement produce the same archive.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/PlanArtifact"},"content_hash":{"type":"string","description":"A plan in your history, instead of sending it."}}}}}},"responses":{"200":{"description":"application/zip"},"400":{"description":"No plan, or more placements than your plan allows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No plan with that hash in your history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The plan does not match its hash, or its verdict, or it is not admissible.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/attestations/verify":{"post":{"summary":"Check a signed attestation. Public: no credential, no quota.","description":"Verifies the Ed25519 signature over the statement's canonical JSON, against the keys this deployment publishes — never against the key id the envelope carries. Free, because a signature only persuades somebody who can check it without an account here. `palletizer attest verify` does the same offline.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"The attestation envelope, either directly or as { attestation: … }.","properties":{"attestation":{"type":"object"}}}}}},"responses":{"200":{"description":"{ signature_valid, reason?, key_id?, statement?, verdict?, checked_against }"},"503":{"description":"This deployment publishes no attestation keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/attestation-keys":{"get":{"summary":"The public key attestations are signed with","security":[],"responses":{"200":{"description":"{ keys: [{ key_id, alg, public_key }] }"}}}},"/api/v1/keys":{"get":{"summary":"Your API keys (prefixes only). Signed-in session only.","responses":{"200":{"description":"Keys"},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create an API key. The key is returned once. Signed-in session only.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":60}}}}}},"responses":{"201":{"description":"The key, once"},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys/{id}":{"delete":{"summary":"Revoke a key. Signed-in session only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Revoked"},"401":{"description":"No credential, or an unknown or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Your plan does not include this. `error.entitlement` names what is missing (see ENTITLEMENTS.md), and `error.upgrade_to` names the cheapest plan that covers it — absent when none does.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No active key with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A quota or rate is spent. `Retry-After` says when to try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Accounts are not configured on this deployment, or the account store did not answer. Nothing was granted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/openapi.json":{"get":{"summary":"This document","security":[],"responses":{"200":{"description":"OpenAPI 3.1"}}}}}}