{"openapi":"3.1.0","info":{"title":"Fits In The Box — V1 Public API","description":"Platform-agnostic 3D bin packing API. Authenticate with a Bearer API key (prefix `fitb_`) obtained via `/v1/register`.","version":"0.1.0"},"paths":{"/v1/register":{"post":{"tags":["V1 API"],"summary":"Register for an API key","description":"Create a new API account and receive an API key. The key is returned exactly once — store it securely. Rate limited per IP address.","operationId":"register_v1_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResponse"}}}},"409":{"description":"Email already registered"},"429":{"description":"Registration rate limit exceeded"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/pack":{"post":{"tags":["V1 API"],"summary":"Pack items into boxes","description":"Run the 3D bin packing algorithm. Supports two modes:\n\n- **Raw mode**: Provide item dimensions and box configurations inline.\n- **Product ID mode**: Reference synced Shopify products by ID; the shop's saved box configs are used automatically.\n\nRequires a valid API key via `Authorization: Bearer fitb_...` header.","operationId":"pack_v1_pack_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1PackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1PackResponse"}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"API account not authorized for this shop"},"404":{"description":"Shop or product not found"},"422":{"description":"Validation error (missing boxes, mixed modes, etc.)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"429":{"description":"Rate limit or monthly quota exceeded"}}}},"/v1/keys/rotate":{"post":{"tags":["V1 API"],"summary":"Rotate API key","description":"Generate a new API key and schedule the old key for expiration. The old key remains valid for 24 hours (grace period).","operationId":"rotate_key_v1_keys_rotate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateKeyResponse"}}}},"401":{"description":"Missing or invalid API key"},"429":{"description":"Rate limit or monthly quota exceeded"}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"PackedBox":{"properties":{"box_name":{"type":"string","title":"Box Name"},"shipping_cost":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Shipping Cost"},"utilization":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Utilization"},"remaining_volume":{"type":"number","title":"Remaining Volume"},"remaining_weight":{"type":"number","title":"Remaining Weight"},"total_package_weight":{"type":"number","title":"Total Package Weight"},"items_packed":{"items":{"$ref":"#/components/schemas/PackedItem"},"type":"array","title":"Items Packed"}},"type":"object","required":["box_name","shipping_cost","utilization","remaining_volume","remaining_weight","total_package_weight","items_packed"],"title":"PackedBox"},"PackedItem":{"properties":{"product_id":{"type":"string","title":"Product Id"},"title":{"type":"string","title":"Title"},"position":{"prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"}],"type":"array","maxItems":3,"minItems":3,"title":"Position"},"dimensions":{"prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"}],"type":"array","maxItems":3,"minItems":3,"title":"Dimensions"},"weight":{"type":"number","title":"Weight"}},"type":"object","required":["product_id","title","position","dimensions","weight"],"title":"PackedItem"},"RegisterRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Email address for the account"},"shop_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shop Domain","description":"Optional Shopify store domain to link to this account","examples":["my-store.myshopify.com"]}},"type":"object","required":["email"],"title":"RegisterRequest","description":"Request body for API key registration.","examples":[{"email":"user@example.com"}]},"RegisterResponse":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"API key (shown once — store it securely)","examples":["fitb_live_abc123..."]},"email":{"type":"string","title":"Email","description":"Registered email address"},"tier":{"type":"string","title":"Tier","description":"Account tier","examples":["free"]},"quota_limit":{"type":"integer","title":"Quota Limit","description":"Monthly request quota","examples":[5000]},"quota_reset_at":{"type":"string","format":"date-time","title":"Quota Reset At","description":"When the monthly quota resets (UTC)"}},"type":"object","required":["api_key","email","tier","quota_limit","quota_reset_at"],"title":"RegisterResponse","description":"Response after successful registration. The API key is shown exactly once."},"RotateKeyResponse":{"properties":{"new_api_key":{"type":"string","title":"New Api Key","description":"New API key (shown once — store it securely)"},"old_key_expires_at":{"type":"string","format":"date-time","title":"Old Key Expires At","description":"Old key remains valid until this time (24h grace period)"}},"type":"object","required":["new_api_key","old_key_expires_at"],"title":"RotateKeyResponse","description":"Response returned after rotating an API key."},"V1BoxConfig":{"properties":{"name":{"type":"string","title":"Name","description":"Box name","examples":["Small Flat Rate"]},"length":{"type":"number","title":"Length","description":"Internal box length","examples":[12.0]},"width":{"type":"number","title":"Width","description":"Internal box width","examples":[10.0]},"height":{"type":"number","title":"Height","description":"Internal box height","examples":[5.0]},"max_weight":{"type":"number","title":"Max Weight","description":"Maximum total weight the box can hold","examples":[160.0]},"box_weight":{"type":"number","title":"Box Weight","description":"Weight of the empty box itself","default":0,"examples":[8.0]},"packing_material_weight":{"type":"number","title":"Packing Material Weight","description":"Weight of packing material","default":0,"examples":[4.0]},"dimension_unit":{"anyOf":[{"type":"string","enum":["IN","FT","CM","M","MM"]},{"type":"null"}],"title":"Dimension Unit","description":"Unit for box dimensions (defaults to IN)"},"weight_unit":{"anyOf":[{"type":"string","enum":["OZ","LB","G","KG"]},{"type":"null"}],"title":"Weight Unit","description":"Unit for box weights (defaults to OZ)"},"shipping_cost":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Shipping Cost","description":"Shipping cost for this box","default":"0","examples":["5.99"]}},"type":"object","required":["name","length","width","height","max_weight"],"title":"V1BoxConfig","description":"A box configuration for raw dimension mode."},"V1PackItem":{"properties":{"length":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Length","description":"Item length","examples":[10.0]},"width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Width","description":"Item width","examples":[8.0]},"height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height","description":"Item height","examples":[5.0]},"weight":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Weight","description":"Item weight","examples":[16.0]},"dimension_unit":{"anyOf":[{"type":"string","enum":["IN","FT","CM","M","MM"]},{"type":"null"}],"title":"Dimension Unit","description":"Unit for length/width/height (defaults to IN)"},"weight_unit":{"anyOf":[{"type":"string","enum":["OZ","LB","G","KG"]},{"type":"null"}],"title":"Weight Unit","description":"Unit for weight (defaults to OZ)"},"product_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Id","description":"Shopify product ID (use instead of raw dimensions)","examples":["gid://shopify/Product/123456"]},"quantity":{"type":"integer","minimum":1.0,"title":"Quantity","description":"Number of this item to pack","default":1}},"type":"object","title":"V1PackItem","description":"An item to pack. Provide either raw dimensions or a product_id (not both)."},"V1PackRequest":{"properties":{"items":{"items":{"$ref":"#/components/schemas/V1PackItem"},"type":"array","minItems":1,"title":"Items","description":"Items to pack (at least one)"},"boxes":{"anyOf":[{"items":{"$ref":"#/components/schemas/V1BoxConfig"},"type":"array"},{"type":"null"}],"title":"Boxes","description":"Box configurations (required for raw mode, omit for product ID mode)"},"shop_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shop Domain","description":"Shopify store domain (required for product ID mode)","examples":["my-store.myshopify.com"]}},"type":"object","required":["items"],"title":"V1PackRequest","description":"Pack items into boxes.\n\nTwo modes:\n- **Raw mode**: Provide `items` with dimensions and `boxes` with configurations.\n- **Product ID mode**: Provide `items` with `product_id` and a `shop_domain`.\n  The shop's saved box configurations are used automatically."},"V1PackResponse":{"properties":{"boxes":{"items":{"$ref":"#/components/schemas/PackedBox"},"type":"array","title":"Boxes","description":"Boxes used with their packed items"},"total_shipping":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Total Shipping","description":"Total shipping cost across all boxes"},"total_items":{"type":"integer","title":"Total Items","description":"Total number of items packed"},"unfitted_items":{"items":{"type":"string"},"type":"array","title":"Unfitted Items","description":"Product IDs that could not fit in any box","default":[]}},"type":"object","required":["boxes","total_shipping","total_items"],"title":"V1PackResponse","description":"Packing result with box assignments, shipping cost, and any unfitted items."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}