{"openapi":"3.1.0","info":{"title":"Lovstudio Public API","version":"1.0.0","summary":"Programmatic access to Lovstudio creative tools and the Lovstudio tool plugin catalog.","description":"All endpoints authenticate with a Bearer API key (`sk_live_*`) created at /account/api-keys. Calls are billed against the owning user's credit balance. Generation cost: 70 credits per call. Tool plugins use protocol lovstudio.tool.v1.","contact":{"name":"Lovstudio","url":"https://0.0.0.0:3000"}},"servers":[{"url":"https://0.0.0.0:3000/api/v1"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"Lovstudio API key (sk_live_...)"}},"schemas":{"StyleRemixRequest":{"type":"object","description":"Provide exactly one of `image_base64` or `image_url`. The image must be a clear photo of a single person's face/upper body.","properties":{"image_base64":{"type":"string","description":"Base64-encoded image bytes (raw, or as a `data:image/...;base64,...` URL). Max ~10MB decoded."},"mime_type":{"type":"string","description":"MIME type of `image_base64`. Defaults to `image/jpeg` if omitted.","examples":["image/jpeg","image/png","image/webp"]},"image_url":{"type":"string","format":"uri","description":"Public HTTPS URL to fetch the image from. Must be reachable from Lovstudio servers."},"style":{"type":"string","enum":["shougongchuan"],"default":"shougongchuan","description":"Style preset. Currently only `shougongchuan` (手工川 line-art portrait) is supported."}},"examples":[{"image_url":"https://example.com/photo.jpg","style":"shougongchuan"}]},"StyleRemixResponse":{"type":"object","required":["image_base64","mime_type","style","credits_spent"],"properties":{"image_base64":{"type":"string","description":"Generated image as base64 (no `data:` prefix)."},"mime_type":{"type":"string","examples":["image/png"]},"style":{"type":"string","enum":["shougongchuan"]},"credits_spent":{"type":"integer","examples":[70]},"upstream_text":{"type":["string","null"],"description":"Optional commentary from the model. Usually null."}}},"ToolPluginManifest":{"type":"object","required":["id","version","publisher","category","runtime","status","infra","billing","protocol"],"properties":{"id":{"type":"string","examples":["shougongchuan-style"]},"i18nKey":{"type":"string","examples":["shougongchuanStyle"]},"version":{"type":"string","examples":["0.3.0"]},"publisher":{"type":"string","examples":["lovstudio"]},"category":{"type":"string","enum":["ai","content","data","design","developer","productivity"]},"runtime":{"type":"string","enum":["client","server","api","external"]},"status":{"type":"string","enum":["stable","beta","experimental"]},"keywords":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"array","items":{"type":"string"}},"infra":{"type":"object","required":["auth","credits","protocol","deploy","scopes"],"properties":{"auth":{"type":"string","enum":["none","optional","required","apiKey"]},"credits":{"type":"string","enum":["none","optional","metered"]},"protocol":{"type":"string","enum":["lovstudio.tool.v1"]},"deploy":{"type":"string","enum":["hosted","external","hybrid"]},"scopes":{"type":"array","items":{"type":"string"}}}},"billing":{"type":"object","required":["model"],"properties":{"model":{"type":"string","enum":["free","credits"]},"creditsPerRun":{"type":"number"},"unit":{"type":"string"},"refund":{"type":"string","enum":["automatic","manual","none"]}}},"protocol":{"type":"object","required":["inputs","outputs"],"properties":{"inputs":{"type":"array","items":{"type":"string"}},"outputs":{"type":"array","items":{"type":"string"}},"endpoints":{"type":"array","items":{"type":"object","required":["method","path","operationId","auth"],"properties":{"method":{"type":"string"},"path":{"type":"string"},"operationId":{"type":"string"},"auth":{"type":"string","enum":["none","session","apiKey"]},"credits":{"type":"number"}}}}}}}},"Error":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","description":"Stable machine-readable error code. Use this for branching, not the human message.","examples":["missing_authorization","invalid_api_key","invalid_json","missing_image","invalid_image","invalid_style","insufficient_credits","upstream_unreachable","upstream_error","no_image"]},"message":{"type":"string","description":"Human-readable explanation."},"required":{"type":"integer","description":"Credits required (only on insufficient_credits)."},"details":{"type":"string","description":"Upstream error body (only on upstream_error)."}}}}},"security":[{"ApiKeyAuth":[]}],"paths":{"/tool-plugins":{"get":{"operationId":"listToolPlugins","summary":"List Lovstudio tool plugins","description":"Returns the public lovstudio.tool.v1 plugin catalog. Supports category, runtime, billing, and sort query filters.","security":[],"parameters":[{"name":"category","in":"query","schema":{"type":"string","enum":["ai","content","data","design","developer","productivity"]}},{"name":"runtime","in":"query","schema":{"type":"string","enum":["client","server","api","external"]}},{"name":"billing","in":"query","schema":{"type":"string","enum":["free","credits"]}},{"name":"source","in":"query","schema":{"type":"string","enum":["all","built-in","community"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["rank","updated","votes","views","name"]}}],"responses":{"200":{"description":"Plugin catalog.","content":{"application/json":{"schema":{"type":"object","required":["protocol","plugins"],"properties":{"protocol":{"type":"string","enum":["lovstudio.tool.v1"]},"docs":{"type":"string","examples":["/api-docs#tool-plugin-protocol"]},"filters":{"type":"object"},"plugins":{"type":"array","items":{"$ref":"#/components/schemas/ToolPluginManifest"}}}}}}}}},"post":{"operationId":"publishToolPlugin","summary":"Publish a community tool plugin manifest","description":"Publishes a lovstudio.tool.v1 manifest to the shared community catalog. The current v1 catalog stores manifest/API/external-runtime plugins only; Lovstudio does not execute uploaded zip bundles.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolPluginManifest"}}}},"responses":{"201":{"description":"Plugin published.","content":{"application/json":{"schema":{"type":"object","properties":{"plugin":{"$ref":"#/components/schemas/ToolPluginManifest"}}}}}},"401":{"description":"Login required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid manifest.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Database migration required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/style-remix":{"post":{"operationId":"remixPortraitInShougongchuanStyle","summary":"Redraw a portrait photo in the 手工川 line-art style","description":"Takes one input photo of a person and returns a black-line-on-orange portrait that matches the 手工川 reference style. Synchronous: typical latency 10–30 seconds. Costs 70 credits per successful call. On upstream failure the call is automatically refunded.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StyleRemixRequest"}}}},"responses":{"200":{"description":"Successful generation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StyleRemixResponse"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (missing or malformed image, unknown style, etc).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream model failed. Credits already refunded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}