{"components":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying (sent on 429).","schema":{"example":12,"type":"integer"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window (per minute).","schema":{"example":60,"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"example":59,"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix epoch second at which the current window resets.","schema":{"example":1893456000,"type":"integer"}}},"schemas":{"AppDetail":{"properties":{"description":{"example":"Turn a selfie into a studio headshot.","type":"string"},"id":{"example":"507f1f77bcf86cd799439011","type":"string"},"inputs":{"description":"Input field schema; each item's nodeId is the key used in the run request inputs map.","items":{"type":"object"},"type":"array"},"outputs":{"items":{"type":"object"},"type":"array"},"params":{"description":"The app's exposed generation variables (advanced options), if any. inputs[] is unaffected — params is a separate, additive array. Omit a param from a run's `params` object to use its `default`.","items":{"properties":{"control":{"description":"Suggested UI control (e.g. \"select\", \"slider\", \"switch\").","type":"string"},"default":{"description":"The node's value at publish time — used when the param is omitted from a run."},"description":{"type":"string"},"enumValues":{"description":"Present when kind is \"enum\".","items":{"properties":{"label":{"type":"string"},"value":{"description":"A string, number, or boolean."}},"type":"object"},"type":"array"},"group":{"description":"The node title this param belongs to — use it to group params into an advanced-options section.","example":"Hero video","type":"string"},"kind":{"enum":["text","number","boolean","enum"],"type":"string"},"label":{"example":"Resolution","type":"string"},"max":{"description":"Present when kind is \"number\" and an upper bound was published.","type":"number"},"min":{"description":"Present when kind is \"number\" and a lower bound was published.","type":"number"},"name":{"description":"The key to use in the run request's `params` object.","example":"hero-video-resolution","type":"string"},"nodeId":{"type":"string"},"step":{"type":"number"}},"type":"object"},"type":"array"},"published":{"example":true,"type":"boolean"},"slug":{"example":"headshot-generator","type":"string"},"thumbnailUrl":{"example":"https://cdn.tryflowy.ai/apps/headshot.png","type":"string"},"title":{"example":"Headshot generator","type":"string"},"workflow":{"description":"Sanitized workflow structure of the app's graph — display fields only (never prompts, params, or the raw executable graph). Absent when the app has no captured graph.","properties":{"edges":{"items":{"properties":{"source":{"example":"node_1","type":"string"},"target":{"example":"node_2","type":"string"}},"type":"object"},"type":"array"},"nodes":{"items":{"properties":{"id":{"example":"node_1","type":"string"},"label":{"example":"Studio headshot","type":"string"},"text":{"description":"Example text content for text nodes, truncated to 400 characters. Display-only.","example":"A studio headshot with soft lighting","type":"string"},"thumb":{"description":"Preview media URL for the node, when one was captured at publish.","example":"https://cdn.tryflowy.ai/apps/node-1.png","type":"string"},"type":{"example":"image","type":"string"},"x":{"example":120,"type":"number"},"y":{"example":40,"type":"number"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"},"AppSummary":{"properties":{"description":{"example":"Turn a selfie into a studio headshot.","type":"string"},"id":{"example":"507f1f77bcf86cd799439011","type":"string"},"published":{"example":true,"type":"boolean"},"runCount":{"example":42,"type":"integer"},"slug":{"example":"headshot-generator","type":"string"},"thumbnailUrl":{"example":"https://cdn.tryflowy.ai/apps/headshot.png","type":"string"},"title":{"example":"Headshot generator","type":"string"}},"type":"object"},"Asset":{"properties":{"createdAt":{"example":"2026-01-01T00:00:00Z","format":"date-time","type":"string"},"durationSeconds":{"example":5,"type":"number"},"fileName":{"example":"mug.png","type":"string"},"height":{"example":1024,"type":"integer"},"id":{"example":"64c3f2a1e8b9c0d1f2e3a4b5","type":"string"},"mediaType":{"enum":["image","video","audio","flam"],"example":"image","type":"string"},"prompt":{"description":"The generation prompt, for generated assets.","type":"string"},"sizeBytes":{"example":524288,"type":"integer"},"source":{"enum":["upload","generated"],"example":"generated","type":"string"},"url":{"description":"Signed, downloadable media URL — expires.","example":"https://cdn.tryflowy.ai/assets/mug.png","type":"string"},"width":{"example":1024,"type":"integer"}},"type":"object"},"AssetList":{"properties":{"assets":{"items":{"$ref":"#/components/schemas/Asset"},"type":"array"},"page":{"example":1,"type":"integer"},"pageSize":{"example":20,"type":"integer"},"total":{"example":42,"type":"integer"},"totalPages":{"example":3,"type":"integer"}},"type":"object"},"CreditBalance":{"properties":{"available":{"description":"credits - blockedCredits.","example":4880,"type":"number"},"blockedCredits":{"description":"Credits reserved by in-flight generations/runs.","example":120,"type":"number"},"credits":{"description":"Total credits on the workspace wallet.","example":5000,"type":"number"},"workspaceId":{"example":"507f1f77bcf86cd799439011","type":"string"}},"type":"object"},"Error":{"properties":{"code":{"description":"Stable machine-readable error code, present for structured input errors: invalid_body (malformed JSON or wrong shape for inputs/params), unknown_param, invalid_param.","example":"invalid_param","type":"string"},"details":{"description":"Present alongside `code` for structured input errors.","properties":{"expected":{"example":"a number between 2 and 15","type":"string"},"field":{"example":"hero-video-duration","type":"string"},"received":{"description":"The value that failed validation (any JSON scalar)."}},"type":"object"},"error":{"example":"this API key lacks the runs:write scope","type":"string"}},"required":["error"],"type":"object"},"GenerationAccepted":{"properties":{"data":{"properties":{"estimatedCredits":{"description":"Credits reserved for this generation; the settled charge arrives on the generation view.","example":12,"type":"number"},"generationId":{"example":"64c3f2a1e8b9c0d1f2e3a4b5","type":"string"},"model":{"example":"gemini-3-flash","type":"string"},"status":{"enum":["processing"],"example":"processing","type":"string"},"type":{"enum":["image","video"],"example":"image","type":"string"}},"type":"object"}},"type":"object"},"GenerationRequest":{"properties":{"aspect_ratio":{"description":"image: 1:1 (default), 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9. video: 16:9 (default), 9:16, 1:1.","example":"1:1","type":"string"},"duration_seconds":{"description":"Video only. Default 5; clamped to 4–10.","example":5,"type":"number"},"input_media":{"description":"Optional reference media: an asset from your workspace library (asset_id) or a public URL. Video generations accept up to 9 images, 3 videos and 3 audio references (12 total).","items":{"properties":{"asset_id":{"example":"64c3f2a1e8b9c0d1f2e3a4b5","type":"string"},"url":{"example":"https://example.com/reference.jpg","type":"string"}},"type":"object"},"type":"array"},"model":{"description":"image: gemini-3-flash (default). video: seedance-2.0 (default).","example":"gemini-3-flash","type":"string"},"prompt":{"example":"a studio photo of a ceramic mug on a marble counter","type":"string"},"resolution":{"description":"image: 1K (default), 2K, 4K. video: 480p, 720p (default), 1080p, 4k.","example":"1K","type":"string"},"type":{"enum":["image","video"],"example":"image","type":"string"}},"required":["type","prompt"],"type":"object"},"GenerationView":{"properties":{"data":{"properties":{"completedAt":{"example":"2026-01-01T00:01:15Z","format":"date-time","type":"string"},"createdAt":{"example":"2026-01-01T00:00:00Z","format":"date-time","type":"string"},"creditsCharged":{"description":"Credits actually deducted; 0 until the generation completes.","example":12,"type":"number"},"error":{"description":"Human-readable failure reason. Present only when status is failed.","type":"string"},"generationId":{"example":"64c3f2a1e8b9c0d1f2e3a4b5","type":"string"},"model":{"example":"gemini-3-flash","type":"string"},"outputs":{"items":{"properties":{"assetId":{"description":"Library asset id, when the output was recorded as an asset.","type":"string"},"durationSeconds":{"example":5,"type":"number"},"height":{"example":1024,"type":"integer"},"kind":{"enum":["image","video"],"example":"image","type":"string"},"url":{"description":"Signed, downloadable media URL — expires.","example":"https://cdn.tryflowy.ai/generations/mug.png","type":"string"},"width":{"example":1024,"type":"integer"}},"type":"object"},"type":"array"},"progress":{"description":"Best-effort live progress snapshot; present only while processing.","nullable":true},"prompt":{"example":"a studio photo of a ceramic mug on a marble counter","type":"string"},"status":{"enum":["processing","completed","failed"],"example":"completed","type":"string"},"type":{"enum":["image","video"],"example":"image","type":"string"}},"type":"object"}},"type":"object"},"RunResponse":{"properties":{"data":{"properties":{"runId":{"example":"64c3f2a1e8b9c0d1f2e3a4b5","type":"string"},"status":{"enum":["queued","running","completed","failed"],"example":"queued","type":"string"},"warnings":{"description":"Present only when non-empty. Legacy leniency signals about the `inputs[]` surface: an unrecognized inputs[].node_id was ignored, or a required content input was missing and the app's published content was used instead. Never causes the run to fail.","items":{"properties":{"code":{"enum":["unknown_input","missing_input"],"type":"string"},"field":{"type":"string"},"message":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"},"RunView":{"properties":{"data":{"properties":{"appId":{"example":"507f1f77bcf86cd799439011","type":"string"},"appTitle":{"example":"Headshot generator","type":"string"},"createdAt":{"example":"2026-01-01T00:00:00Z","format":"date-time","type":"string"},"error":{"description":"Human-readable failure reason. Present only when status is failed.","type":"string"},"outputs":{"items":{"properties":{"kind":{"enum":["image","video","audio","text","flam"],"example":"image","type":"string"},"nodeId":{"example":"node_9","type":"string"},"text":{"description":"Text result — present for text outputs.","type":"string"},"url":{"description":"Signed, downloadable media URL — present for media outputs; expires.","example":"https://cdn.tryflowy.ai/runs/headshot.png","type":"string"}},"type":"object"},"type":"array"},"runId":{"example":"64c3f2a1e8b9c0d1f2e3a4b5","type":"string"},"status":{"enum":["queued","running","completed","failed"],"example":"completed","type":"string"},"updatedAt":{"example":"2026-01-01T00:00:05Z","format":"date-time","type":"string"}},"type":"object"}},"type":"object"},"V1Account":{"properties":{"available":{"type":"number"},"blockedCredits":{"type":"number"},"credits":{"type":"number"},"email":{"type":"string"},"name":{"type":"string"},"planId":{"description":"Empty/absent means the free plan","type":"string"},"planRenewsAt":{"format":"date-time","type":"string"},"planStatus":{"type":"string"},"userId":{"type":"string"},"workspaceId":{"description":"The account wallet (personal workspace)","type":"string"}},"type":"object"},"V1Project":{"properties":{"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"isFavorite":{"type":"boolean"},"thumbnailUrl":{"type":"string"},"title":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"workspaceId":{"type":"string"}},"type":"object"},"V1Transaction":{"properties":{"amount":{"type":"number"},"completedAt":{"format":"date-time","type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"nodeType":{"type":"string"},"status":{"enum":["blocked","deducted","refunded","failed","granted","revoked"],"type":"string"},"workspaceId":{"description":"The wallet that paid","type":"string"}},"type":"object"},"V1Workspace":{"properties":{"isPersonal":{"type":"boolean"},"name":{"type":"string"},"role":{"enum":["owner","editor","viewer"],"type":"string"},"workspaceId":{"type":"string"}},"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"bearerFormat":"flowy_*","description":"A workspace API key. Secret keys (`flowy_…`) are server-side — keep them private. Publishable keys (`flowy_pk_…`) are browser-safe but only work from their allowlisted domains.","scheme":"bearer","type":"http"}}},"info":{"description":"Run Flowy apps and direct generations programmatically.\n\nAuthenticate with a workspace API key in the `Authorization: Bearer` header. A **secret** key (`flowy_…`) is for servers; a **publishable** key (`flowy_pk_…`) is browser-safe and only works from its allowlisted domains. Every run or generation bills the API key's own workspace credits (caller-pays), so a leaked key can never spend another workspace's balance.\n\nKeys carry **scopes**: `apps:read` (list/inspect apps), `apps:write` (create/publish apps), `runs:read` (poll runs), `runs:write` (start runs), `generations:read` (poll direct generations), `generations:write` (start direct generations), `assets:read` (list/read workspace assets), and `credits:read` (read the credit balance). A request that needs a scope the key doesn't hold returns `403`.\n\nA published (public) app is runnable by any key; a private app is runnable only by a key of its own workspace.","title":"Flowy App API","version":"1.4.0"},"openapi":"3.0.3","paths":{"/v1/account":{"get":{"description":"The account behind the key: email, name, plan, and the account wallet's credit balance. Keys are account-bound, so this identifies who authorized the connection.","operationId":"getAccount","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"$ref":"#/components/schemas/V1Account"}},"type":"object"}}},"description":"The account"}},"summary":"Get the signed-in account","tags":["Account"]}},"/v1/account/transactions":{"get":{"description":"The account's credit ledger (entries where this user was the actor, across every wallet), newest first.","operationId":"listAccountTransactions","parameters":[{"in":"query","name":"size","required":false,"schema":{"default":20,"maximum":100,"type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"default":0,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"offset":{"type":"integer"},"size":{"type":"integer"},"transactions":{"items":{"$ref":"#/components/schemas/V1Transaction"},"type":"array"}},"type":"object"}},"type":"object"}}},"description":"The transactions"}},"summary":"List the account's credit transactions","tags":["Account"]}},"/v1/apps":{"get":{"description":"Lists the active apps in the authenticated key's workspace. **Requires the `apps:read` scope.**","operationId":"listApps","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AppSummary"},"type":"array"}},"type":"object"}}},"description":"The apps in your workspace.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the apps:read scope"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit exceeded","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"summary":"List apps","tags":["Apps"]},"post":{"description":"Publishes a canvas project as a reusable app in the key's workspace, mirroring the Studio create flow. Pass `publish: true` to make it live immediately (otherwise it is created as a draft, runnable only via the API, by a key of the same workspace). **Requires the `apps:write` scope.**","operationId":"createApp","requestBody":{"content":{"application/json":{"example":{"description":"Turn a selfie into a studio headshot.","input_node_ids":["node_1"],"output_node_ids":["node_9"],"publish":true,"title":"Headshot generator"},"schema":{"properties":{"description":{"type":"string"},"executable_graph":{"description":"The captured executable closure of the marked inputs → outputs, as produced by the flow builder's publish step.","type":"object"},"input_node_ids":{"items":{"type":"string"},"type":"array"},"output_node_ids":{"items":{"type":"string"},"type":"array"},"param_schema":{"description":"The derived form/output schema ({ inputs, outputs, params }), as produced by the flow builder's publish step.","type":"object"},"publish":{"description":"true to make the app live immediately; otherwise it is created as a draft. Defaults to false.","type":"boolean"},"source_project_id":{"description":"The canvas project this app is derived from. Optional — consulted only as a thumbnail fallback.","type":"string"},"thumbnail_url":{"type":"string"},"title":{"example":"Headshot generator","type":"string"}},"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"data":{"$ref":"#/components/schemas/AppDetail"}},"type":"object"}}},"description":"The created app.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request body."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the apps:write scope"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit exceeded","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"summary":"Create an app","tags":["Apps"]}},"/v1/apps/{appId}":{"get":{"description":"Returns an app's identity, its input/output schema, and the sanitized `workflow` structure (display nodes + edges) of its graph. Use each input's `nodeId` as the key in the run request's `inputs` map. Allowed when the app is published (any key) or belongs to the key's own workspace. **Requires the `apps:read` scope.**","operationId":"getApp","parameters":[{"example":"507f1f77bcf86cd799439011","in":"path","name":"appId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"$ref":"#/components/schemas/AppDetail"}},"type":"object"}}},"description":"App detail with input/output schema.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Private app not in your workspace, or the key lacks the apps:read scope"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"App not found"}},"summary":"Get an app","tags":["Apps"]}},"/v1/apps/{appId}/runs":{"post":{"description":"Starts an asynchronous run, billed to the key's workspace. Returns immediately with a `runId` — poll `GET /v1/runs/{runId}` for the result. Omit the body for an app with no inputs. `inputs` identifies each value by the `nodeId` from `GET /v1/apps/{appId}`: an unrecognized `node_id` is ignored (not rejected) and a missing required content input falls back to the app's published content — both surface as a `warnings` entry on the response, never a 400. `params` (optional) sets the app's exposed generation variables (advanced options, from `GET /v1/apps/{appId}`'s `params[]`); unlike `inputs`, an unknown param name or an out-of-constraint value is rejected with `400 unknown_param` / `400 invalid_param`. **Requires the `runs:write` scope.**","operationId":"startRun","parameters":[{"example":"507f1f77bcf86cd799439011","in":"path","name":"appId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"examples":{"contentOnly":{"summary":"Content input only","value":{"inputs":[{"asset_url":"https://example.com/me.jpg","node_id":"node_1"}]}},"variadicInput":{"summary":"A variadic (multi-value) content input","value":{"inputs":[{"asset_url":["https://example.com/a.jpg","https://example.com/b.jpg"],"node_id":"references"}]}},"withParams":{"summary":"Content input plus advanced options","value":{"inputs":[{"asset_url":"https://example.com/me.jpg","node_id":"node_1"}],"params":{"hero-video-duration":8,"hero-video-enable-audio":true,"hero-video-resolution":"1080p"}}}},"schema":{"properties":{"inputs":{"description":"Get each nodeId from GET /v1/apps/{appId}. Preferred form: an array with one entry per input. Also accepted: an object mapping nodeId -\u003e value(s) directly.","oneOf":[{"items":{"properties":{"asset_url":{"description":"A media input's value (a URL). A string, or an array of strings for a variadic (multi-value) input.","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"node_id":{"type":"string"},"prompt":{"description":"A text input's value. A string, or an array of strings for a variadic (multi-value) input.","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]}},"required":["node_id"],"type":"object"},"type":"array"},{"additionalProperties":{"oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"description":"Accepted alias: node_id -\u003e value(s).","type":"object"}]},"params":{"additionalProperties":{"description":"A string, number, or boolean."},"description":"Advanced options: map of paramSchema `params[].name` -\u003e value. Omit a param to use its published default. Get names/kinds/constraints from GET /v1/apps/{appId}'s `params[]`. Strict: an unrecognized key or an out-of-constraint value is rejected (see the 400 response).","type":"object"}},"type":"object"}}},"required":false},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunResponse"}}},"description":"Run accepted. Poll GET /v1/runs/{runId}.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Malformed JSON body or wrong shape for `inputs`/`params` (`invalid_body`), unknown params key (`unknown_param`), or a value that fails its kind/enum/min/max/step constraint (`invalid_param`). See `code` and `details` on the error body. The legacy `inputs[]` surface never causes a 400 for unknown or missing entries."},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Daily spend cap reached or insufficient workspace credits"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Private app not in your workspace, or the key lacks the runs:write scope"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"App not found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or concurrency limit exceeded","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"summary":"Start a run","tags":["Runs"]}},"/v1/assets":{"get":{"description":"Lists the authenticated key's workspace asset library (uploads + generated media), newest first, with signed download URLs. **Requires the `assets:read` scope.**","operationId":"listAssets","parameters":[{"in":"query","name":"media_type","required":false,"schema":{"enum":["image","video","audio"],"type":"string"}},{"description":"Matches file names, tags and generation prompts.","in":"query","name":"search","required":false,"schema":{"type":"string"}},{"in":"query","name":"page","required":false,"schema":{"default":1,"type":"integer"}},{"in":"query","name":"page_size","required":false,"schema":{"default":20,"maximum":100,"type":"integer"}},{"description":"Optional workspace context (from GET /v1/workspaces); default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"$ref":"#/components/schemas/AssetList"}},"type":"object"}}},"description":"One page of workspace assets.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid media_type"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the assets:read scope"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit exceeded","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"summary":"List assets","tags":["Assets"]}},"/v1/assets/{assetId}":{"get":{"description":"Returns one workspace asset with a signed download URL. Only assets of your own workspace are visible. **Requires the `assets:read` scope.**","operationId":"getAsset","parameters":[{"example":"64c3f2a1e8b9c0d1f2e3a4b5","in":"path","name":"assetId","required":true,"schema":{"type":"string"}},{"description":"Optional workspace context (from GET /v1/workspaces); default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"$ref":"#/components/schemas/Asset"}},"type":"object"}}},"description":"The asset.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the assets:read scope"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Asset not found (or belongs to another workspace)"}},"summary":"Get an asset","tags":["Assets"]}},"/v1/brandkits":{"get":{"operationId":"listBrandKits","parameters":[{"description":"Optional workspace context; default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The workspace's brand kits (colors, fonts, logos)"}},"summary":"List brand kits","tags":["Brand kits"]},"post":{"description":"Extract a brand kit from a URL (async; poll /v1/brandkits/extract/{jobId}).","operationId":"createBrandKit","requestBody":{"content":{"application/json":{"schema":{"properties":{"url":{"type":"string"},"workspace_id":{"type":"string"}},"required":["url"],"type":"object"}}},"required":true},"responses":{"202":{"description":"Extraction job started"}},"summary":"Create a brand kit from a website","tags":["Brand kits"]}},"/v1/brandkits/extract/{jobId}":{"get":{"operationId":"pollBrandKitExtract","parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Extraction status"}},"summary":"Poll a brand kit extraction","tags":["Brand kits"]}},"/v1/brandkits/{id}":{"delete":{"operationId":"deleteBrandKit","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Optional workspace context; default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"}},"summary":"Delete a brand kit","tags":["Brand kits"]},"get":{"operationId":"getBrandKit","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Optional workspace context; default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The brand kit"}},"summary":"Get a brand kit","tags":["Brand kits"]},"patch":{"operationId":"patchBrandKit","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Optional workspace context; default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Partial brand kit fields (colors, fonts, …)","type":"object"}}},"required":true},"responses":{"200":{"description":"Updated"}},"summary":"Update a brand kit","tags":["Brand kits"]}},"/v1/credits":{"get":{"description":"Returns a credit balance: total, blocked (reserved by in-flight work) and available. Defaults to the authenticated key's own wallet (for account-bound keys, the account wallet). Pass workspace_id to read another workspace the account can manage (owner/editor).","operationId":"getCredits","parameters":[{"description":"Optional workspace id (from GET /v1/workspaces). Default: the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"$ref":"#/components/schemas/CreditBalance"}},"type":"object"}}},"description":"The workspace credit balance.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the credits:read scope"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit exceeded","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"summary":"Get the credit balance","tags":["Credits"]}},"/v1/elements":{"get":{"operationId":"listElements","parameters":[{"description":"Optional workspace context; default is the key's own workspace.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}},{"description":"Set to 'false' for full element detail; default returns summaries.","in":"query","name":"summary","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The workspace's elements (reusable references)"}},"summary":"List elements","tags":["Elements"]},"post":{"description":"Create a reusable element (formerly 'character'). Persists a draft; the identity spec / reference set are filled in later.","operationId":"createElement","requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"type":"string"},"direction":{"type":"string"},"essence":{"type":"string"},"name":{"type":"string"},"source_images":{"items":{"properties":{"asset_id":{"type":"string"},"file_name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"style_medium":{"type":"string"},"workspace_id":{"type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"description":"Element created"}},"summary":"Create an element","tags":["Elements"]}},"/v1/elements/{elementId}":{"get":{"operationId":"getElement","parameters":[{"in":"path","name":"elementId","required":true,"schema":{"type":"string"}},{"in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The element"},"404":{"description":"Not found"}},"summary":"Get an element","tags":["Elements"]}},"/v1/generations":{"post":{"description":"Starts an asynchronous image or video generation, billed to the key's workspace. Returns immediately with a `generationId` — poll `GET /v1/generations/{generationId}` for the result. Completed outputs are also recorded in the workspace asset library. **Requires the `generations:write` scope.**","operationId":"startGeneration","requestBody":{"content":{"application/json":{"example":{"aspect_ratio":"1:1","prompt":"a studio photo of a ceramic mug on a marble counter","resolution":"1K","type":"image"},"schema":{"$ref":"#/components/schemas/GenerationRequest","properties":{"geometry_format":{"description":"3d only; default glb","enum":["glb","usdz","fbx","obj","stl"],"type":"string"},"material":{"description":"3d only; default All","enum":["All","PBR","Shaded"],"type":"string"},"mode":{"description":"Audio only; default sfx","enum":["sfx","music"],"type":"string"},"workspace_id":{"description":"Optionally bill a different workspace the account can manage; default is the key's own wallet.","type":"string"}}}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationAccepted"}}},"description":"Generation accepted. Poll GET /v1/generations/{generationId}.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request (unknown type/model, bad aspect ratio or resolution, bad input media)"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Daily spend cap reached or insufficient workspace credits"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the generations:write scope"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or concurrency limit exceeded","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"summary":"Start a generation","tags":["Generations"]}},"/v1/generations/{generationId}":{"get":{"description":"Returns a generation's status and, once complete, its outputs (signed, downloadable media URLs). Only generations started by your own workspace are visible. **Requires the `generations:read` scope.**","operationId":"getGeneration","parameters":[{"example":"64c3f2a1e8b9c0d1f2e3a4b5","in":"path","name":"generationId","required":true,"schema":{"type":"string"}},{"description":"Optional workspace context (from GET /v1/workspaces); default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationView"}}},"description":"Generation status and outputs.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The key lacks the generations:read scope"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Generation not found (or belongs to another workspace)"}},"summary":"Get a generation","tags":["Generations"]}},"/v1/openapi.json":{"get":{"description":"The machine-readable OpenAPI 3 description of this API. Public — no key required.","operationId":"getOpenAPISpec","responses":{"200":{"content":{"application/json":{}},"description":"OpenAPI 3 document"}},"security":[],"summary":"Get the OpenAPI spec","tags":["Spec"]}},"/v1/projects":{"get":{"description":"The workspace's canvas projects.","operationId":"listProjects","parameters":[{"description":"Optional workspace context; default is the key's own wallet.","in":"query","name":"workspace_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"projects":{"items":{"$ref":"#/components/schemas/V1Project"},"type":"array"},"total":{"type":"integer"}},"type":"object"}},"type":"object"}}},"description":"Projects"}},"summary":"List projects","tags":["Projects"]}},"/v1/projects/{projectId}/nodes":{"post":{"description":"Start a generation as a node on a real project's canvas (spends credits). The node id is minted server-side; output lands in the asset library.","operationId":"createNode","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"aspect_ratio":{"type":"string"},"duration_seconds":{"type":"number"},"input_media":{"items":{"properties":{"asset_id":{"type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"mode":{"enum":["sfx","music"],"type":"string"},"prompt":{"type":"string"},"resolution":{"type":"string"},"type":{"enum":["image","video","audio","3d"],"type":"string"}},"required":["type","prompt"],"type":"object"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"nodeId":{"type":"string"},"projectId":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"type":"object"}},"type":"object"}}},"description":"Node generation started"}},"summary":"Create a node generation","tags":["Projects"]}},"/v1/renders":{"get":{"operationId":"listRenders","parameters":[{"in":"query","name":"project_id","required":false,"schema":{"type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"default":20,"maximum":100,"type":"integer"}}],"responses":{"200":{"description":"Recent renders with video URLs"}},"summary":"List renders","tags":["Renders"]},"post":{"description":"Render a composition timeline (Remotion project JSON) to a final video — the last step of an end-to-end video pipeline (async).","operationId":"startRender","requestBody":{"content":{"application/json":{"schema":{"properties":{"project":{"description":"The composition timeline","type":"object"},"project_id":{"type":"string"},"workspace_id":{"type":"string"}},"required":["project"],"type":"object"}}},"required":true},"responses":{"202":{"description":"Render started"}},"summary":"Render a composition to video","tags":["Renders"]}},"/v1/renders/{workflowId}":{"get":{"description":"One render record by workflow id, any status (processing | completed | failed) — pending and failed renders are visible here.","operationId":"getRender","parameters":[{"in":"path","name":"workflowId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The render record"},"404":{"description":"Not found"}},"summary":"Get a render","tags":["Renders"]}},"/v1/runs/{runId}":{"get":{"description":"Returns a run's status and, once complete, its outputs (signed, downloadable media URLs). Only runs started by your own workspace are visible. **Requires the `runs:read` scope.**","operationId":"getRun","parameters":[{"example":"64c3f2a1e8b9c0d1f2e3a4b5","in":"path","name":"runId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunView"}}},"description":"Run status and outputs.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or missing API key"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Run belongs to another workspace, or the key lacks the runs:read scope"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Run not found"}},"summary":"Get a run","tags":["Runs"]}},"/v1/workspaces":{"get":{"description":"Every workspace the account can act in. Use a workspaceId with GET /v1/credits?workspace_id= to read that workspace's balance.","operationId":"listWorkspaces","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"total":{"type":"integer"},"workspaces":{"items":{"$ref":"#/components/schemas/V1Workspace"},"type":"array"}},"type":"object"}},"type":"object"}}},"description":"The workspaces"}},"summary":"List the account's workspaces","tags":["Account"]}}},"security":[{"ApiKeyAuth":[]}],"servers":[{"description":"Resolved from the request host","url":"https://apis.tryflowy.ai/genstudio-svc-v2/api"}],"tags":[{"description":"Discover apps and inspect their input/output schema.","name":"Apps"},{"description":"Start runs and poll for their results.","name":"Runs"},{"description":"Start direct image/video generations and poll for their results.","name":"Generations"},{"description":"Browse the workspace's asset library (uploads + generated media).","name":"Assets"},{"description":"Read the workspace's credit balance.","name":"Credits"},{"description":"The machine-readable OpenAPI document.","name":"Spec"}]}