{
  "openapi": "3.1.0",
  "info": {
    "title": "Tryveliq API",
    "version": "0.1.0",
    "description": "Paid subscription required. Server-to-server virtual clothing previews and merchant-defined size guidance. Bearer keys are workspace-scoped. Requests share dashboard quotas. Input uploads must be licensed PNG/JPEG images, 32–512 pixels per side, maximum 2 MB. Measurement inputs are not persisted."
  },
  "servers": [
    {
      "url": "https://tryveliq.hakan-olcer.workers.dev/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/products": {
      "get": {
        "operationId": "listProducts",
        "responses": {
          "200": {
            "description": "Workspace products"
          }
        }
      },
      "post": {
        "operationId": "createProduct",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "category": {
                    "enum": [
                      "tops",
                      "bottoms",
                      "dresses",
                      "outerwear"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created; returns id"
          },
          "400": {
            "description": "Invalid input"
          }
        }
      }
    },
    "/products/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "operationId": "updateProduct",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "assetId": {
                    "type": "string",
                    "description": "Garment asset ID in this workspace"
                  },
                  "chart": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/SizeRange"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved"
          },
          "404": {
            "description": "Product or asset not found in this workspace"
          }
        }
      }
    },
    "/assets": {
      "get": {
        "operationId": "listAssets",
        "responses": {
          "200": {
            "description": "Private input assets with temporary viewing URLs"
          }
        }
      },
      "post": {
        "operationId": "uploadAsset",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": true,
            "schema": {
              "enum": [
                "avatar",
                "garment"
              ]
            }
          },
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "X-Image-Rights-Confirmed",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created; returns id and temporary viewing URL"
          },
          "400": {
            "description": "Invalid dimensions, image format or permission acknowledgement"
          },
          "413": {
            "description": "Upload exceeds 2 MB"
          },
          "429": {
            "description": "Workspace asset limit reached"
          }
        }
      }
    },
    "/generations": {
      "get": {
        "operationId": "listGenerations",
        "responses": {
          "200": {
            "description": "Latest 100 workspace generations"
          }
        }
      },
      "post": {
        "operationId": "createGeneration",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "avatarId"
                ],
                "properties": {
                  "productId": {
                    "type": "string"
                  },
                  "avatarId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued job; poll /generations/{id}"
          },
          "402": {
            "description": "Shopify subscription approval required when paid billing is enabled"
          },
          "409": {
            "description": "Idempotency key was used for a different request"
          },
          "429": {
            "description": "Workspace or platform safety limit reached"
          },
          "503": {
            "description": "Generation service paused or queue unavailable"
          }
        }
      }
    },
    "/generations/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getGeneration",
        "responses": {
          "200": {
            "description": "Job with status queued/running/completed/failed. Completed jobs include a temporary image URL expiring in ten minutes; fetch this endpoint to refresh it."
          },
          "404": {
            "description": "Job not found in this workspace"
          }
        }
      }
    },
    "/recommendations": {
      "post": {
        "operationId": "recommendSize",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "productId",
                  "chestCm"
                ],
                "properties": {
                  "productId": {
                    "type": "string"
                  },
                  "chestCm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 300
                  },
                  "waistCm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 300
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Returns size (nullable), status (matched/between_sizes/no_match), candidates, basis and notice. Missing waist when required by the chart returns no_match."
          },
          "400": {
            "description": "Invalid measurements"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "SizeRange": {
        "type": "object",
        "required": [
          "size",
          "chestMin",
          "chestMax"
        ],
        "properties": {
          "size": {
            "type": "string"
          },
          "chestMin": {
            "type": "number"
          },
          "chestMax": {
            "type": "number"
          },
          "waistMin": {
            "type": "number"
          },
          "waistMax": {
            "type": "number"
          }
        },
        "description": "Body measurements in centimeters. Each maximum must exceed its minimum. Both waist bounds must be supplied together. Overlapping rows return between_sizes."
      }
    }
  }
}
