From e9207aa7ccb6f06fce42ef0e42e0d7450bef3b3f Mon Sep 17 00:00:00 2001 From: Quasar of Mikus <159663231+quasar-of-mikus@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:05:24 -0400 Subject: [PATCH 01/21] fix (MultiGPU): prevent freeze on manual abort when using MultiGPU CFG Split (#14235) * fix (MultiGPU): prevent freeze on manual abort when using MultiGPU CFG Split Problem: Upon manual abort application hangs indefinitely. `InterruptProcessingException` inherits from `BaseException` and bypasses MultiGPU's worker error handling block so thread dies silently, leaving the main thread waiting forever for `result_q.get()` Fix: Catch `comfy.model_management.InterruptProcessingException` instead of `Exception` so it's caught and passed back via `result_q` to unblock the main thread when manual abort signal fires. * oops --- comfy/multigpu.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/comfy/multigpu.py b/comfy/multigpu.py index bb9d334d3..2b6d8260d 100644 --- a/comfy/multigpu.py +++ b/comfy/multigpu.py @@ -54,6 +54,8 @@ class MultiGPUThreadPool: try: result = fn(*args, **kwargs) result_q.put((result, None)) + except comfy.model_management.InterruptProcessingException as e: + result_q.put((None, e)) except Exception as e: result_q.put((None, e)) From dc10c0133ebda1d6438c65fb6be9cd5eb20b4434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Sepp=C3=A4nen?= <40791699+kijai@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:40:49 +0300 Subject: [PATCH 02/21] PiD: Add SDXL and QwenImage (#14240) --- comfy_extras/nodes_pid.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/comfy_extras/nodes_pid.py b/comfy_extras/nodes_pid.py index 811b9ae8e..71855254e 100644 --- a/comfy_extras/nodes_pid.py +++ b/comfy_extras/nodes_pid.py @@ -21,8 +21,8 @@ class PiDConditioning(io.ComfyNode): inputs=[ io.Conditioning.Input("positive"), io.Latent.Input("latent", tooltip="latent (from VAEEncode or a KSampler)."), - io.Combo.Input("latent_format", options=["flux", "sd3"], default="flux", - tooltip="Flux1 and Flux2 latents auto-detected from channel dim, sd3 has to be selected manually."), + io.Combo.Input("latent_format", options=["flux", "sd3", "sdxl", "qwenimage"], default="flux", + tooltip="Flux1 (16-ch) and Flux2 (128-ch) latents are auto-detected from channel dim under 'flux'. For SD3 (16-ch), SDXL (4-ch), or QwenImage (16-ch), select manually."), io.Float.Input( "degrade_sigma", default=0.0, min=0.0, max=1.0, step=0.01, tooltip="0 = clean latent. Increase to denoise corrupted latent outputs.", @@ -36,9 +36,17 @@ class PiDConditioning(io.ComfyNode): samples = latent["samples"] if latent_format == "flux": fmt_cls = comfy.latent_formats.Flux2 if samples.shape[1] == 128 else comfy.latent_formats.Flux - else: + elif latent_format == "sd3": fmt_cls = comfy.latent_formats.SD3 + elif latent_format == "sdxl": + fmt_cls = comfy.latent_formats.SDXL + elif latent_format == "qwenimage": + fmt_cls = comfy.latent_formats.Wan21 + else: + raise ValueError(f"Unknown latent_format: {latent_format}") lq_latent = fmt_cls().process_in(samples) + if lq_latent.ndim == 5: + lq_latent = lq_latent[:, :, 0] sigma_t = torch.tensor([float(degrade_sigma)], dtype=torch.float32) return io.NodeOutput(node_helpers.conditioning_set_values( positive, {"lq_latent": lq_latent, "degrade_sigma": sigma_t}, From d4c7ebff9c318698082fed85a0ebf3bdc1fe3d2a Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Tue, 2 Jun 2026 17:52:41 -0700 Subject: [PATCH 03/21] Remove old useless no comfy kitchen fallback. (#14245) * Remove old fallback used when no comfy kitchen. * Remove unused logging import --- comfy/ldm/flux/math.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/comfy/ldm/flux/math.py b/comfy/ldm/flux/math.py index 6d0aed827..891dea7dd 100644 --- a/comfy/ldm/flux/math.py +++ b/comfy/ldm/flux/math.py @@ -4,7 +4,7 @@ from torch import Tensor from comfy.ldm.modules.attention import optimized_attention import comfy.model_management -import logging +import comfy.quant_ops def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None, transformer_options={}) -> Tensor: @@ -44,21 +44,15 @@ def _apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor): return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis) -try: - import comfy.quant_ops - q_apply_rope = comfy.quant_ops.ck.apply_rope - q_apply_rope1 = comfy.quant_ops.ck.apply_rope1 - def apply_rope(xq, xk, freqs_cis): - if comfy.model_management.in_training: - return _apply_rope(xq, xk, freqs_cis) - else: - return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis) - def apply_rope1(x, freqs_cis): - if comfy.model_management.in_training: - return _apply_rope1(x, freqs_cis) - else: - return q_apply_rope1(x, freqs_cis) -except: - logging.warning("No comfy kitchen, using old apply_rope functions.") - apply_rope = _apply_rope - apply_rope1 = _apply_rope1 +def apply_rope(xq, xk, freqs_cis): + if comfy.model_management.in_training: + return _apply_rope(xq, xk, freqs_cis) + else: + return comfy.quant_ops.ck.apply_rope(xq, xk, freqs_cis) + + +def apply_rope1(x, freqs_cis): + if comfy.model_management.in_training: + return _apply_rope1(x, freqs_cis) + else: + return comfy.quant_ops.ck.apply_rope1(x, freqs_cis) From bd7da053aeaa3424828f7a0fb6ebeffeec9f5876 Mon Sep 17 00:00:00 2001 From: rattus <46076784+rattus128@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:57:16 +1000 Subject: [PATCH 04/21] comfy-aimdo: 0.4.8 (#14244) Aimdo 0.4.8 fixes a crash in multi-gpu due to contention on the singleton bounce buffer. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b09d31a8b..7dff9e3c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ SQLAlchemy>=2.0.0 filelock av>=16.0.0 comfy-kitchen==0.2.10 -comfy-aimdo==0.4.7 +comfy-aimdo==0.4.8 requests simpleeval>=1.0.0 blake3 From c7a22e1b4ef3cfa4d2a28acf95323bac0243d99d Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:13:20 +0300 Subject: [PATCH 05/21] [Partner Nodes] feat: add Ideogram V4 node (#14261) Signed-off-by: bigcat88 --- comfy_api_nodes/apis/ideogram.py | 16 +++++ comfy_api_nodes/nodes_ideogram.py | 116 ++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/comfy_api_nodes/apis/ideogram.py b/comfy_api_nodes/apis/ideogram.py index 737e18e3b..c5ad9559f 100644 --- a/comfy_api_nodes/apis/ideogram.py +++ b/comfy_api_nodes/apis/ideogram.py @@ -290,3 +290,19 @@ class IdeogramV3Request(BaseModel): None, description='Optional masks for character reference images. When provided, must match the number of character_reference_images. Each mask should be a grayscale image of the same dimensions as the corresponding character reference image. The images should be in JPEG, PNG or WebP format.' ) + + +class IdeogramV4Request(BaseModel): + text_prompt: str | None = Field( + None, + description="Natural-language prompt; Magic Prompt is applied automatically. " + "Supply exactly one of text_prompt or json_prompt.", + ) + json_prompt: dict[str, Any] | None = Field( + None, + description="Structured V4 prompt object consumed directly (disables Magic Prompt). " + "Supply exactly one of text_prompt or json_prompt.", + ) + resolution: str | None = Field(None, description="Output resolution in WIDTHxHEIGHT (e.g. '2048x2048').") + rendering_speed: str | None = Field(None, description="Rendering speed: 'TURBO', 'DEFAULT', or 'QUALITY'.") + enable_copyright_detection: bool | None = Field(None, description="Opt into post-generation copyright detection.") diff --git a/comfy_api_nodes/nodes_ideogram.py b/comfy_api_nodes/nodes_ideogram.py index 8018c3902..3b914a850 100644 --- a/comfy_api_nodes/nodes_ideogram.py +++ b/comfy_api_nodes/nodes_ideogram.py @@ -10,6 +10,7 @@ from comfy_api_nodes.apis.ideogram import ( ImageRequest, IdeogramV3Request, IdeogramV3EditRequest, + IdeogramV4Request, ) from comfy_api_nodes.util import ( ApiEndpoint, @@ -17,6 +18,7 @@ from comfy_api_nodes.util import ( download_url_as_bytesio, resize_mask_to_image, sync_op, + validate_string, ) V1_V1_RES_MAP = { @@ -798,6 +800,119 @@ class IdeogramV3(IO.ComfyNode): return IO.NodeOutput(await download_and_process_images(image_urls)) +class IdeogramV4(IO.ComfyNode): + + @classmethod + def define_schema(cls): + return IO.Schema( + node_id="IdeogramV4", + display_name="Ideogram V4", + category="partner/image/Ideogram", + description="Generates images using the Ideogram 4.0 model from a text prompt.", + inputs=[ + IO.String.Input( + "prompt", + multiline=True, + default="", + tooltip="Text prompt for the image generation.", + ), + IO.Combo.Input( + "resolution", + options=[ + "Auto", + "2048x2048 (1:1)", + "1440x2880 (1:2)", + "2880x1440 (2:1)", + "1664x2496 (2:3)", + "2496x1664 (3:2)", + "1792x2240 (4:5)", + "2240x1792 (5:4)", + "1440x2560 (9:16)", + "2560x1440 (16:9)", + "1600x2560 (5:8)", + "2560x1600 (8:5)", + "1728x2304 (3:4)", + "2304x1728 (4:3)", + "1296x3168 (9:22)", + "3168x1296 (22:9)", + "1152x2944 (9:23)", + "2944x1152 (23:9)", + "1248x3328 (3:8)", + "3328x1248 (8:3)", + "1280x3072 (5:12)", + "3072x1280 (12:5)", + ], + default="Auto", + ), + IO.Combo.Input( + "rendering_speed", + options=["DEFAULT", "TURBO", "QUALITY"], + default="DEFAULT", + tooltip="Controls the trade-off between generation speed and quality.", + ), + IO.Int.Input( + "seed", + default=0, + min=0, + max=2147483647, + step=1, + control_after_generate=True, + display_mode=IO.NumberDisplay.number, + ), + ], + outputs=[ + IO.Image.Output(), + ], + hidden=[ + IO.Hidden.auth_token_comfy_org, + IO.Hidden.api_key_comfy_org, + IO.Hidden.unique_id, + ], + is_api_node=True, + price_badge=IO.PriceBadge( + depends_on=IO.PriceBadgeDepends(widgets=["rendering_speed"]), + expr=""" + ( + $speed := widgets.rendering_speed; + $price := + $contains($speed,"turbo") ? 0.0429 : + $contains($speed,"quality") ? 0.143 : + 0.0858; + {"type":"usd","usd": $price} + ) + """, + ), + ) + + @classmethod + async def execute( + cls, + prompt: str, + resolution: str, + rendering_speed: str, + seed: int, + ): + validate_string(prompt, strip_whitespace=True, min_length=1) + response = await sync_op( + cls, + ApiEndpoint(path="/proxy/ideogram/ideogram-v4/generate", method="POST"), + response_model=IdeogramGenerateResponse, + data=IdeogramV4Request( + text_prompt=prompt, + resolution=resolution.split(" ")[0] if resolution != "Auto" else None, + rendering_speed=rendering_speed, + ), + max_retries=1, + ) + + if not response.data or len(response.data) == 0: + raise Exception("No images were generated in the response") + image_urls = [image_data.url for image_data in response.data if image_data.url] + if not image_urls: + raise Exception("No image URLs were generated in the response") + return IO.NodeOutput(await download_and_process_images(image_urls)) + + class IdeogramExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[IO.ComfyNode]]: @@ -805,6 +920,7 @@ class IdeogramExtension(ComfyExtension): IdeogramV1, IdeogramV2, IdeogramV3, + IdeogramV4, ] From 24f9a020ce0c0f6966fcb79e5580afbee5706904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Sepp=C3=A4nen?= <40791699+kijai@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:41:44 +0300 Subject: [PATCH 06/21] Support Ideogram4 (#14259) --- comfy/ldm/ideogram4/model.py | 297 +++++++++++++++++++++++++++ comfy/model_base.py | 16 ++ comfy/model_detection.py | 7 + comfy/sd.py | 10 +- comfy/supported_models.py | 40 ++++ comfy/text_encoders/ideogram4.py | 77 +++++++ comfy_extras/nodes_custom_sampler.py | 122 +++++++++++ comfy_extras/nodes_ideogram4.py | 64 ++++++ nodes.py | 3 +- 9 files changed, 633 insertions(+), 3 deletions(-) create mode 100644 comfy/ldm/ideogram4/model.py create mode 100644 comfy/text_encoders/ideogram4.py create mode 100644 comfy_extras/nodes_ideogram4.py diff --git a/comfy/ldm/ideogram4/model.py b/comfy/ldm/ideogram4/model.py new file mode 100644 index 000000000..3b02a243a --- /dev/null +++ b/comfy/ldm/ideogram4/model.py @@ -0,0 +1,297 @@ +""" +The Ideogram 4 transformer is a NextDiT/Lumina2-family single-stream model +consumes Qwen3-VL hidden-state features (concatenated from 13 layers -> 53248 dims) +packs ``[text tokens, image tokens]`` into one sequence with block-diagonal segment attention and 3D interleaved MRoPE. +""" + +from __future__ import annotations + +import math + +import torch +import torch.nn as nn +import torch.nn.functional as F + +import comfy.patcher_extension +from comfy.ldm.lumina.model import FeedForward +from comfy.ldm.modules.attention import optimized_attention_masked +from comfy.text_encoders.llama import apply_rope, precompute_freqs_cis + +# Per-token role indicators +SEQUENCE_PADDING_INDICATOR = -1 +OUTPUT_IMAGE_INDICATOR = 2 +LLM_TOKEN_INDICATOR = 3 +# Image grid coordinates are offset so they never collide with text positions +IMAGE_POSITION_OFFSET = 65536 + + +class Ideogram4Attention(nn.Module): + def __init__(self, hidden_size, num_heads, eps=1e-5, dtype=None, device=None, operations=None): + super().__init__() + self.num_heads = num_heads + self.head_dim = hidden_size // num_heads + self.hidden_size = hidden_size + + self.qkv = operations.Linear(hidden_size, hidden_size * 3, bias=False, dtype=dtype, device=device) + self.norm_q = operations.RMSNorm(self.head_dim, eps=eps, elementwise_affine=True, dtype=dtype, device=device) + self.norm_k = operations.RMSNorm(self.head_dim, eps=eps, elementwise_affine=True, dtype=dtype, device=device) + self.o = operations.Linear(hidden_size, hidden_size, bias=False, dtype=dtype, device=device) + + def forward(self, x, attn_mask, freqs_cis, transformer_options={}): + batch_size, seq_len, _ = x.shape + qkv = self.qkv(x).view(batch_size, seq_len, 3, self.num_heads, self.head_dim) + q, k, v = qkv.unbind(dim=2) + + q = self.norm_q(q) + k = self.norm_k(k) + + # (B, heads, L, head_dim) + q = q.transpose(1, 2) + k = k.transpose(1, 2) + v = v.transpose(1, 2) + + q, k = apply_rope(q, k, freqs_cis) + + out = optimized_attention_masked(q, k, v, self.num_heads, attn_mask, skip_reshape=True, transformer_options=transformer_options) + return self.o(out) + + +class Ideogram4TransformerBlock(nn.Module): + def __init__(self, hidden_size, intermediate_size, num_heads, norm_eps, adaln_dim, dtype=None, device=None, operations=None): + super().__init__() + self.attention = Ideogram4Attention(hidden_size, num_heads, eps=1e-5, dtype=dtype, device=device, operations=operations) + self.feed_forward = FeedForward( + dim=hidden_size, hidden_dim=intermediate_size, multiple_of=1, ffn_dim_multiplier=None, + operation_settings={"operations": operations, "dtype": dtype, "device": device}, + ) + + self.attention_norm1 = operations.RMSNorm(hidden_size, eps=norm_eps, elementwise_affine=True, dtype=dtype, device=device) + self.ffn_norm1 = operations.RMSNorm(hidden_size, eps=norm_eps, elementwise_affine=True, dtype=dtype, device=device) + self.attention_norm2 = operations.RMSNorm(hidden_size, eps=norm_eps, elementwise_affine=True, dtype=dtype, device=device) + self.ffn_norm2 = operations.RMSNorm(hidden_size, eps=norm_eps, elementwise_affine=True, dtype=dtype, device=device) + + self.adaln_modulation = operations.Linear(adaln_dim, 4 * hidden_size, bias=True, dtype=dtype, device=device) + + def forward(self, x, attn_mask, freqs_cis, adaln_input, transformer_options={}): + mod = self.adaln_modulation(adaln_input) + scale_msa, gate_msa, scale_mlp, gate_mlp = mod.chunk(4, dim=-1) + gate_msa = torch.tanh(gate_msa) + gate_mlp = torch.tanh(gate_mlp) + scale_msa = 1.0 + scale_msa + scale_mlp = 1.0 + scale_mlp + + attn_out = self.attention(self.attention_norm1(x) * scale_msa, attn_mask, freqs_cis, transformer_options=transformer_options) + x = x + gate_msa * self.attention_norm2(attn_out) + x = x + gate_mlp * self.ffn_norm2(self.feed_forward(self.ffn_norm1(x) * scale_mlp)) + return x + + +def _sinusoidal_embedding(t, dim, scale=1e4): + t = t.to(torch.float32) + half = dim // 2 + freq = math.log(scale) / (half - 1) + freq = torch.exp(torch.arange(half, dtype=torch.float32, device=t.device) * -freq) + emb = t.unsqueeze(-1) * freq + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1) + if dim % 2 == 1: + emb = F.pad(emb, (0, 1)) + return emb + + +class Ideogram4EmbedScalar(nn.Module): + def __init__(self, dim, input_range=(0.0, 1.0), dtype=None, device=None, operations=None): + super().__init__() + self.dim = dim + self.range_min, self.range_max = input_range + self.mlp_in = operations.Linear(dim, dim, bias=True, dtype=dtype, device=device) + self.mlp_out = operations.Linear(dim, dim, bias=True, dtype=dtype, device=device) + + def forward(self, x): + x = x.to(torch.float32) + scaled = 1e4 * (x - self.range_min) / (self.range_max - self.range_min) + emb = _sinusoidal_embedding(scaled, self.dim) + emb = emb.to(self.mlp_in.weight.dtype) + emb = F.silu(self.mlp_in(emb)) + return self.mlp_out(emb) + + +class Ideogram4FinalLayer(nn.Module): + def __init__(self, hidden_size, out_channels, adaln_dim, dtype=None, device=None, operations=None): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, eps=1e-6, elementwise_affine=False, dtype=dtype, device=device) + self.linear = operations.Linear(hidden_size, out_channels, bias=True, dtype=dtype, device=device) + self.adaln_modulation = operations.Linear(adaln_dim, hidden_size, bias=True, dtype=dtype, device=device) + + def forward(self, x, c): + scale = 1.0 + self.adaln_modulation(F.silu(c)) + return self.linear(self.norm_final(x) * scale) + + +class Ideogram4Transformer(nn.Module): + """A single Ideogram 4 backbone operating on a packed token sequence.""" + + def __init__(self, emb_dim, num_layers, num_heads, intermediate_size, adaln_dim, + in_channels, llm_features_dim, rope_theta, mrope_section, norm_eps, + dtype=None, device=None, operations=None): + super().__init__() + self.head_dim = emb_dim // num_heads + self.rope_theta = rope_theta + self.mrope_section = tuple(mrope_section) + + self.input_proj = operations.Linear(in_channels, emb_dim, bias=True, dtype=dtype, device=device) + self.llm_cond_norm = operations.RMSNorm(llm_features_dim, eps=1e-6, elementwise_affine=True, dtype=dtype, device=device) + self.llm_cond_proj = operations.Linear(llm_features_dim, emb_dim, bias=True, dtype=dtype, device=device) + self.t_embedding = Ideogram4EmbedScalar(emb_dim, input_range=(0.0, 1.0), dtype=dtype, device=device, operations=operations) + self.adaln_proj = operations.Linear(emb_dim, adaln_dim, bias=True, dtype=dtype, device=device) + + self.embed_image_indicator = operations.Embedding(2, emb_dim, dtype=dtype, device=device) + + self.layers = nn.ModuleList([ + Ideogram4TransformerBlock(emb_dim, intermediate_size, num_heads, norm_eps, adaln_dim, + dtype=dtype, device=device, operations=operations) + for _ in range(num_layers) + ]) + + self.final_layer = Ideogram4FinalLayer(emb_dim, in_channels, adaln_dim, dtype=dtype, device=device, operations=operations) + + def _backbone(self, llm_features, x, t, position_ids, attn_mask, indicator, transformer_options={}): + indicator = indicator.to(torch.long) + output_image_mask = (indicator == OUTPUT_IMAGE_INDICATOR).to(x.dtype).unsqueeze(-1) + + x = x * output_image_mask + h = self.input_proj(x) * output_image_mask + + t_cond = self.t_embedding(t) + if t.dim() == 1: + t_cond = t_cond.unsqueeze(1) + adaln_input = F.silu(self.adaln_proj(t_cond)) + + # h is zero on the text rows (content lives only on image rows), add writes the text features in place + if llm_features is not None: + L_text = llm_features.shape[1] + text_mask = (indicator[:, :L_text] == LLM_TOKEN_INDICATOR).to(x.dtype).unsqueeze(-1) + llm = self.llm_cond_norm(llm_features * text_mask) + llm = self.llm_cond_proj(llm) * text_mask + h[:, :L_text] = h[:, :L_text] + llm + + h = h + self.embed_image_indicator((indicator == OUTPUT_IMAGE_INDICATOR).to(torch.long)) + + # Qwen3-VL interleaved MRoPE; position_ids (B, L, 3) -> (3, L) (same across batch). + freqs_cis = precompute_freqs_cis( + self.head_dim, position_ids[0].transpose(0, 1), self.rope_theta, + rope_dims=self.mrope_section, interleaved_mrope=True, device=position_ids.device, + ) + + if attn_mask is not None and attn_mask.dtype == torch.bool: + attn_mask = torch.zeros_like(attn_mask, dtype=h.dtype).masked_fill_(~attn_mask, -torch.finfo(h.dtype).max) + + for layer in self.layers: + h = layer(h, attn_mask, freqs_cis, adaln_input, transformer_options=transformer_options) + + return self.final_layer(h, adaln_input) + + +class Ideogram4Transformer2DModel(Ideogram4Transformer): + """Ideogram 4 single-stream DiT. + + Runs a packed ``[text, image]`` sequence when text context is supplied, or an image-only sequence when ``context is None``. + """ + + def __init__(self, image_model=None, in_channels=128, num_layers=34, num_attention_heads=18, attention_head_dim=256, intermediate_size=12288, + adaln_dim=512, llm_features_dim=53248, rope_theta=5000000, mrope_section=(24, 20, 20), norm_eps=1e-5, + dtype=None, device=None, operations=None, **kwargs): + emb_dim = num_attention_heads * attention_head_dim + super().__init__( + emb_dim=emb_dim, num_layers=num_layers, num_heads=num_attention_heads, + intermediate_size=intermediate_size, adaln_dim=adaln_dim, in_channels=in_channels, + llm_features_dim=llm_features_dim, rope_theta=rope_theta, mrope_section=mrope_section, + norm_eps=norm_eps, dtype=dtype, device=device, operations=operations) + self.dtype = dtype + self.in_channels = in_channels + self.out_channels = in_channels + # 128-dim token = patch (2x2) * ae_channels (32). + self.patch_size = 2 + self.ae_channels = in_channels // (self.patch_size * self.patch_size) + + def _img_to_tokens(self, x): + B, C, gh, gw = x.shape + x = x.view(B, self.ae_channels, self.patch_size, self.patch_size, gh, gw) + x = x.permute(0, 4, 5, 2, 3, 1) # (B, gh, gw, pi, pj, c) + return x.reshape(B, gh * gw, C) + + def _tokens_to_img(self, tokens, gh, gw): + B = tokens.shape[0] + C = tokens.shape[-1] + x = tokens.reshape(B, gh, gw, self.patch_size, self.patch_size, self.ae_channels) + x = x.permute(0, 5, 3, 4, 1, 2) # (B, c, pi, pj, gh, gw) + return x.reshape(B, C, gh, gw) + + def _image_position_ids(self, gh, gw, device): + h_idx = torch.arange(gh, device=device).view(-1, 1).expand(gh, gw).reshape(-1) + w_idx = torch.arange(gw, device=device).view(1, -1).expand(gh, gw).reshape(-1) + t_idx = torch.zeros_like(h_idx) + return torch.stack([t_idx, h_idx, w_idx], dim=1) + IMAGE_POSITION_OFFSET # (L_img, 3) + + def _run_conditional(self, x_chunk, context_chunk, attn_mask_chunk, t_chunk, gh, gw, transformer_options): + B = x_chunk.shape[0] + device = x_chunk.device + img_tokens = self._img_to_tokens(x_chunk).to(self.dtype) + L_img = img_tokens.shape[1] + L_text = context_chunk.shape[1] + L = L_text + L_img + latent_dim = img_tokens.shape[-1] + + x_full = torch.zeros(B, L, latent_dim, dtype=img_tokens.dtype, device=device) + x_full[:, L_text:] = img_tokens + + text_pos = torch.arange(L_text, device=device).view(-1, 1).expand(L_text, 3) + img_pos = self._image_position_ids(gh, gw, device) + position_ids = torch.cat([text_pos, img_pos], dim=0).unsqueeze(0).expand(B, L, 3) + + indicator = torch.empty(B, L, dtype=torch.long, device=device) + indicator[:, :L_text] = LLM_TOKEN_INDICATOR + indicator[:, L_text:] = OUTPUT_IMAGE_INDICATOR + + attn_mask = None + if attn_mask_chunk is not None: + segment_ids = torch.ones(B, L, dtype=torch.long, device=device) + pad = (attn_mask_chunk == 0) + segment_ids[:, :L_text][pad] = SEQUENCE_PADDING_INDICATOR + indicator[:, :L_text][pad] = 0 + # Block-diagonal mask from segment ids: (B, 1, L, L), True = attend. + attn_mask = (segment_ids.unsqueeze(2) == segment_ids.unsqueeze(1)).unsqueeze(1) + + out = self._backbone(context_chunk, x_full, t_chunk, position_ids, attn_mask, indicator, + transformer_options=transformer_options) + return self._tokens_to_img(out[:, L_text:], gh, gw) + + def _run_image_only(self, x_chunk, t_chunk, gh, gw, transformer_options): + B = x_chunk.shape[0] + device = x_chunk.device + img_tokens = self._img_to_tokens(x_chunk).to(self.dtype) + L_img = img_tokens.shape[1] + + position_ids = self._image_position_ids(gh, gw, device).unsqueeze(0).expand(B, L_img, 3) + indicator = torch.full((B, L_img), OUTPUT_IMAGE_INDICATOR, dtype=torch.long, device=device) + + # Image-only sequence is a single segment -> no mask, full attention, no LLM context. + out = self._backbone(None, img_tokens, t_chunk, position_ids, None, indicator, transformer_options=transformer_options) + return self._tokens_to_img(out, gh, gw) + + def forward(self, x, timesteps, context=None, attention_mask=None, transformer_options={}, **kwargs): + return comfy.patcher_extension.WrapperExecutor.new_class_executor( + self._forward, + self, + comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options), + ).execute(x, timesteps, context, attention_mask, transformer_options, **kwargs) + + def _forward(self, x, timesteps, context=None, attention_mask=None, transformer_options={}, **kwargs): + bs, c, gh, gw = x.shape + + timesteps = 1.0 - timesteps + + # unconditional pass + if context is None: + return -self._run_image_only(x, timesteps, gh, gw, transformer_options) + + return -self._run_conditional(x, context, attention_mask, timesteps, gh, gw, transformer_options) diff --git a/comfy/model_base.py b/comfy/model_base.py index 3e2d4e930..042804771 100644 --- a/comfy/model_base.py +++ b/comfy/model_base.py @@ -55,6 +55,7 @@ import comfy.ldm.pixeldit.pid import comfy.ldm.ace.model import comfy.ldm.omnigen.omnigen2 import comfy.ldm.qwen_image.model +import comfy.ldm.ideogram4.model import comfy.ldm.kandinsky5.model import comfy.ldm.anima.model import comfy.ldm.ace.ace_step15 @@ -2018,6 +2019,21 @@ class QwenImage(BaseModel): out['ref_latents'] = list([1, 16, sum(map(lambda a: math.prod(a.size()), ref_latents)) // 16]) return out +class Ideogram4(BaseModel): + def __init__(self, model_config, model_type=ModelType.FLOW, device=None): + super().__init__(model_config, model_type, device=device, unet_model=comfy.ldm.ideogram4.model.Ideogram4Transformer2DModel) + + def extra_conds(self, **kwargs): + out = super().extra_conds(**kwargs) + attention_mask = kwargs.get("attention_mask", None) + if attention_mask is not None: + if torch.numel(attention_mask) != attention_mask.sum(): + out['attention_mask'] = comfy.conds.CONDRegular(attention_mask) + cross_attn = kwargs.get("cross_attn", None) + if cross_attn is not None: + out['c_crossattn'] = comfy.conds.CONDRegular(cross_attn) + return out + class HunyuanImage21(BaseModel): def __init__(self, model_config, model_type=ModelType.FLOW, device=None): super().__init__(model_config, model_type, device=device, unet_model=comfy.ldm.hunyuan_video.model.HunyuanVideo) diff --git a/comfy/model_detection.py b/comfy/model_detection.py index 24e742a7f..74c838d13 100644 --- a/comfy/model_detection.py +++ b/comfy/model_detection.py @@ -815,6 +815,13 @@ def detect_unet_config(state_dict, key_prefix, metadata=None): dit_config["default_ref_method"] = "negative_index" return dit_config + if '{}embed_image_indicator.weight'.format(key_prefix) in state_dict_keys: # Ideogram 4 + dit_config = {} + dit_config["image_model"] = "ideogram4" + dit_config["in_channels"] = state_dict['{}input_proj.weight'.format(key_prefix)].shape[1] + dit_config["num_layers"] = count_blocks(state_dict_keys, '{}layers.'.format(key_prefix) + '{}.') + return dit_config + if '{}visual_transformer_blocks.0.cross_attention.key_norm.weight'.format(key_prefix) in state_dict_keys: # Kandinsky 5 dit_config = {} model_dim = state_dict['{}visual_embeddings.in_layer.bias'.format(key_prefix)].shape[0] diff --git a/comfy/sd.py b/comfy/sd.py index 9a2d31930..a66ba1bfb 100644 --- a/comfy/sd.py +++ b/comfy/sd.py @@ -58,6 +58,7 @@ import comfy.text_encoders.omnigen2 import comfy.text_encoders.qwen_image import comfy.text_encoders.hunyuan_image import comfy.text_encoders.z_image +import comfy.text_encoders.ideogram4 import comfy.text_encoders.ovis import comfy.text_encoders.kandinsky5 import comfy.text_encoders.jina_clip_2 @@ -1298,6 +1299,7 @@ class CLIPType(Enum): COGVIDEOX = 27 LENS = 28 PIXELDIT = 29 + IDEOGRAM4 = 30 @@ -1596,8 +1598,12 @@ def load_text_encoder_state_dicts(state_dicts=[], embedding_directory=None, clip clip_target.clip = comfy.text_encoders.ovis.te(**llama_detect(clip_data)) clip_target.tokenizer = comfy.text_encoders.ovis.OvisTokenizer elif te_model == TEModel.QWEN3_8B: - clip_target.clip = comfy.text_encoders.flux.klein_te(**llama_detect(clip_data), model_type="qwen3_8b") - clip_target.tokenizer = comfy.text_encoders.flux.KleinTokenizer8B + if clip_type == CLIPType.IDEOGRAM4: + clip_target.clip = comfy.text_encoders.ideogram4.te(**llama_detect(clip_data)) + clip_target.tokenizer = comfy.text_encoders.ideogram4.Ideogram4Tokenizer + else: + clip_target.clip = comfy.text_encoders.flux.klein_te(**llama_detect(clip_data), model_type="qwen3_8b") + clip_target.tokenizer = comfy.text_encoders.flux.KleinTokenizer8B elif te_model == TEModel.JINA_CLIP_2: clip_target.clip = comfy.text_encoders.jina_clip_2.JinaClip2TextModelWrapper clip_target.tokenizer = comfy.text_encoders.jina_clip_2.JinaClip2TokenizerWrapper diff --git a/comfy/supported_models.py b/comfy/supported_models.py index 0872b0e27..478489ed8 100644 --- a/comfy/supported_models.py +++ b/comfy/supported_models.py @@ -24,6 +24,7 @@ import comfy.text_encoders.qwen_image import comfy.text_encoders.hunyuan_image import comfy.text_encoders.kandinsky5 import comfy.text_encoders.z_image +import comfy.text_encoders.ideogram4 import comfy.text_encoders.anima import comfy.text_encoders.ace15 import comfy.text_encoders.longcat_image @@ -1746,6 +1747,44 @@ class Omnigen2(supported_models_base.BASE): hunyuan_detect = comfy.text_encoders.hunyuan_video.llama_detect(state_dict, "{}qwen25_3b.transformer.".format(pref)) return supported_models_base.ClipTarget(comfy.text_encoders.omnigen2.Omnigen2Tokenizer, comfy.text_encoders.omnigen2.te(**hunyuan_detect)) +class Ideogram4(supported_models_base.BASE): + unet_config = { + "image_model": "ideogram4", + } + + sampling_settings = { + "multiplier": 1.0, + "shift": 1.0, + } + + memory_usage_factor = 1.8 # TODO + + unet_extra_config = { + "num_attention_heads": 18, + "attention_head_dim": 256, + "intermediate_size": 12288, + "adaln_dim": 512, + "llm_features_dim": 53248, + "rope_theta": 5000000, + "mrope_section": [24, 20, 20], + "norm_eps": 1e-5, + } + latent_format = latent_formats.Flux2 + + supported_inference_dtypes = [torch.bfloat16, torch.float32] + + vae_key_prefix = ["vae."] + text_encoder_key_prefix = ["text_encoders."] + + def get_model(self, state_dict, prefix="", device=None): + out = model_base.Ideogram4(self, device=device) + return out + + def clip_target(self, state_dict={}): + pref = self.text_encoder_key_prefix[0] + hunyuan_detect = comfy.text_encoders.hunyuan_video.llama_detect(state_dict, "{}qwen3vl_8b.transformer.".format(pref)) + return supported_models_base.ClipTarget(comfy.text_encoders.ideogram4.Ideogram4Tokenizer, comfy.text_encoders.ideogram4.te(**hunyuan_detect)) + class QwenImage(supported_models_base.BASE): unet_config = { "image_model": "qwen_image", @@ -2233,6 +2272,7 @@ models = [ ACEStep15, Omnigen2, QwenImage, + Ideogram4, Flux2, Lens, Kandinsky5Image, diff --git a/comfy/text_encoders/ideogram4.py b/comfy/text_encoders/ideogram4.py new file mode 100644 index 000000000..55e655d67 --- /dev/null +++ b/comfy/text_encoders/ideogram4.py @@ -0,0 +1,77 @@ +"""Ideogram 4 text encoder: Qwen3-VL-8B language model, 13-layer tap. + +Ideogram 4 conditions on the concatenation of hidden states from 13 layers of +Qwen3-VL (layers 0,3,...,33,35), giving a 4096*13 = 53248-dim feature per token. +""" + +import os + +from transformers import Qwen2Tokenizer + +import comfy.text_encoders.llama +from comfy import sd1_clip + +# Reference taps outputs of layers (0,3,...,35); comfy captures layer inputs, offset by +1. +IDEOGRAM4_TAP_LAYERS = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 36] + + +class Qwen3VLTokenizer(sd1_clip.SDTokenizer): + def __init__(self, embedding_directory=None, tokenizer_data={}): + tokenizer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "qwen25_tokenizer") + super().__init__(tokenizer_path, pad_with_end=False, embedding_directory=embedding_directory, + embedding_size=4096, embedding_key='qwen3vl_8b', tokenizer_class=Qwen2Tokenizer, + has_start_token=False, has_end_token=False, pad_to_max_length=False, + max_length=99999999, min_length=1, pad_token=151643, tokenizer_data=tokenizer_data) + + +class Ideogram4Tokenizer(sd1_clip.SD1Tokenizer): + def __init__(self, embedding_directory=None, tokenizer_data={}): + super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, + name="qwen3vl_8b", tokenizer=Qwen3VLTokenizer) + + self.llama_template = "<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n" + + def tokenize_with_weights(self, text, return_word_ids=False, llama_template=None, **kwargs): + if llama_template is None: + llama_text = self.llama_template.format(text) + else: + llama_text = llama_template.format(text) + return super().tokenize_with_weights(llama_text, return_word_ids=return_word_ids, disable_weights=True, **kwargs) + + +# Qwen3-VL-8B = 5e6 (vs plain Qwen3-8B's 1e6) +# final_norm/lm_head off -> Ideogram only reads raw tapped hidden states +QWEN3VL_8B_CONFIG = {"rope_theta": 5000000.0, "final_norm": False, "lm_head": False} + + +class Qwen3VL8BModel(sd1_clip.SDClipModel): + def __init__(self, device="cpu", layer="hidden", layer_idx=None, dtype=None, attention_mask=True, model_options={}): + super().__init__(device=device, layer=IDEOGRAM4_TAP_LAYERS, layer_idx=None, + textmodel_json_config=dict(QWEN3VL_8B_CONFIG), + dtype=dtype, special_tokens={"pad": 151643}, layer_norm_hidden_state=False, + model_class=comfy.text_encoders.llama.Qwen3_8B, + enable_attention_masks=attention_mask, return_attention_masks=attention_mask, + model_options=model_options) + + +class Ideogram4TEModel(sd1_clip.SD1ClipModel): + def __init__(self, device="cpu", dtype=None, model_options={}): + super().__init__(device=device, dtype=dtype, name="qwen3vl_8b", clip_model=Qwen3VL8BModel, model_options=model_options) + + def encode_token_weights(self, token_weight_pairs): + out, pooled, extra = super().encode_token_weights(token_weight_pairs) + b, n, seq, h = out.shape # (B, n_taps=13, seq, 4096) stacked in ascending layer order. + out = out.permute(0, 2, 3, 1).reshape(b, seq, h * n) # (B, seq, 4096*13). permute -> (B, seq, H, taps). + return out, pooled, extra + + +def te(dtype_llama=None, llama_quantization_metadata=None): + class Ideogram4TEModel_(Ideogram4TEModel): + def __init__(self, device="cpu", dtype=None, model_options={}): + if dtype_llama is not None: + dtype = dtype_llama + if llama_quantization_metadata is not None: + model_options = model_options.copy() + model_options["quantization_metadata"] = llama_quantization_metadata + super().__init__(device=device, dtype=dtype, model_options=model_options) + return Ideogram4TEModel_ diff --git a/comfy_extras/nodes_custom_sampler.py b/comfy_extras/nodes_custom_sampler.py index c3346bf09..b790d7aac 100644 --- a/comfy_extras/nodes_custom_sampler.py +++ b/comfy_extras/nodes_custom_sampler.py @@ -1,5 +1,7 @@ import math import comfy.samplers +import comfy.sampler_helpers +import comfy.patcher_extension import comfy.sample from comfy.k_diffusion import sampling as k_diffusion_sampling from comfy.k_diffusion import sa_solver @@ -894,6 +896,83 @@ class DualCFGGuider(io.ComfyNode): get_guider = execute +class Guider_DualModel(comfy.samplers.CFGGuider): + # Runs the positive (cond) pass on the main model and the negative (uncond) pass on a separate model + def __init__(self, model_patcher, uncond_model_patcher): + super().__init__(model_patcher) + self.uncond_model_patcher = uncond_model_patcher + self.uncond_inner = None + + def outer_sample(self, noise, latent_image, sampler, sigmas, denoise_mask=None, callback=None, disable_pbar=False, seed=None, latent_shapes=None): + self.uncond_inner = None + self.uncond_loaded = [] + self._uncond_neg = None + # skip at cfg 1.0 + if not math.isclose(self.cfg, 1.0): + uc = {"negative": list(map(lambda a: a.copy(), self.conds["negative"]))} + self.uncond_inner, uc, self.uncond_loaded = comfy.sampler_helpers.prepare_sampling( + self.uncond_model_patcher, noise.shape, uc, self.uncond_model_patcher.model_options) + self._uncond_neg = uc["negative"] + self.uncond_model_patcher.pre_run() + try: + return super().outer_sample(noise, latent_image, sampler, sigmas, denoise_mask, callback, disable_pbar, seed, latent_shapes=latent_shapes) + finally: + if self.uncond_inner is not None: + self.uncond_model_patcher.cleanup() + comfy.sampler_helpers.cleanup_models({"negative": self._uncond_neg}, self.uncond_loaded) + self.uncond_inner = None + + def inner_sample(self, noise, latent_image, device, sampler, sigmas, denoise_mask, callback, disable_pbar, seed, latent_shapes=None): + if self.uncond_inner is not None: + li = latent_image + if li is not None and torch.count_nonzero(li) > 0: + li = self.uncond_inner.process_latent_in(li) + self._uncond_conds = comfy.samplers.process_conds( + self.uncond_inner, noise, {"negative": self._uncond_neg}, device, li, denoise_mask, seed, latent_shapes=latent_shapes)["negative"] + return super().inner_sample(noise, latent_image, device, sampler, sigmas, denoise_mask, callback, disable_pbar, seed, latent_shapes=latent_shapes) + + def predict_noise(self, x, timestep, model_options={}, seed=None): + positive = self.conds.get("positive", None) + if self.uncond_inner is None: # cfg == 1 or no negative -> single model, cond only + return comfy.samplers.calc_cond_batch(self.inner_model, [positive], x, timestep, model_options)[0] + cond = comfy.samplers.calc_cond_batch(self.inner_model, [positive], x, timestep, model_options)[0] + + uncond_model_options = model_options + if "multigpu_clones" in model_options: # TODO: support multigpu instead of just running uncond on a single GPU + uncond_model_options = {k: v for k, v in model_options.items() if k != "multigpu_clones"} + uncond = comfy.samplers.calc_cond_batch(self.uncond_inner, [self._uncond_conds], x, timestep, uncond_model_options)[0] + return comfy.samplers.cfg_function(self.inner_model, cond, uncond, self.cfg, x, timestep, + model_options=model_options, cond=positive, uncond=self._uncond_conds) + +class DualModelGuider(io.ComfyNode): + @classmethod + def define_schema(cls): + return io.Schema( + node_id="DualModelGuider", + display_name="Dual Model CFG Guider", + category="model/sampling/guiders", + inputs=[ + io.Model.Input("model", tooltip="Model used for the positive (conditional) pass."), + io.Model.Input("model_negative", optional=True, tooltip="Model used for the negative (unconditional) pass. Use the same model for ordinary CFG."), + io.Conditioning.Input("positive"), + io.Float.Input("cfg", default=4.0, min=0.0, max=100.0, step=0.1, round=0.01), + io.Conditioning.Input("negative", optional=True, tooltip="Negative conditioning run on the negative model. Leave unconnected for a text-free (image-only) unconditional pass."), + ], + outputs=[io.Guider.Output()], + ) + + @classmethod + def execute(cls, model, positive, cfg, model_negative=None, negative=None) -> io.NodeOutput: + if negative is None: + negative = [[None, {}]] # null cond -> no cross_attn -> model runs image-only + + guider = Guider_DualModel(model, model_negative) if model_negative is not None else comfy.samplers.CFGGuider(model) + guider.set_conds(positive, negative) + guider.set_cfg(cfg) + return io.NodeOutput(guider) + + get_guider = execute + class DisableNoise(io.ComfyNode): @classmethod def define_schema(cls): @@ -1054,11 +1133,53 @@ class ManualSigmas(io.ComfyNode): sigmas = torch.FloatTensor(sigmas) return io.NodeOutput(sigmas) +class CFGOverride(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="CFGOverride", + display_name="CFG Override", + description="Override cfg to a fixed value over a [start, end] percent slice of the steps. " + "With multiple overrides, the one nearest the sampler wins on overlap.", + category="sampling/custom_sampling", + inputs=[ + io.Model.Input("model"), + io.Float.Input("cfg", default=1.0, min=0.0, max=100.0, step=0.1, round=0.01), + io.Float.Input("start_percent", default=0.0, min=0.0, max=1.0, step=0.001), + io.Float.Input("end_percent", default=1.0, min=0.0, max=1.0, step=0.001), + ], + outputs=[io.Model.Output()], + ) + + @classmethod + def execute(cls, model, cfg, start_percent, end_percent) -> io.NodeOutput: + ms = model.get_model_object("model_sampling") + sigma_hi = ms.percent_to_sigma(start_percent) # percent->sigma decreasing, so hi >= lo + sigma_lo = ms.percent_to_sigma(end_percent) + + def predict_noise_wrapper(executor, *args, **kwargs): + sigma = float(args[1].flatten()[0]) # args = (x, timestep, model_options, seed) + if not (sigma_lo <= sigma <= sigma_hi): + return executor(*args, **kwargs) + guider = executor.class_obj # guider.cfg feeds cond_scale + saved = guider.cfg + guider.cfg = cfg + try: + return executor(*args, **kwargs) + finally: + guider.cfg = saved # restore for other steps/overrides + + m = model.clone() + m.add_wrapper(comfy.patcher_extension.WrappersMP.PREDICT_NOISE, predict_noise_wrapper) + return io.NodeOutput(m) + + class CustomSamplersExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[io.ComfyNode]]: return [ SamplerCustom, + CFGOverride, BasicScheduler, KarrasScheduler, ExponentialScheduler, @@ -1087,6 +1208,7 @@ class CustomSamplersExtension(ComfyExtension): SamplingPercentToSigma, CFGGuider, DualCFGGuider, + DualModelGuider, BasicGuider, RandomNoise, DisableNoise, diff --git a/comfy_extras/nodes_ideogram4.py b/comfy_extras/nodes_ideogram4.py new file mode 100644 index 000000000..d5827db4f --- /dev/null +++ b/comfy_extras/nodes_ideogram4.py @@ -0,0 +1,64 @@ +"""Ideogram 4 sampling helper +""" + +import math + +import torch +from typing_extensions import override +from comfy_api.latest import ComfyExtension, io + +_LOGSNR_MIN = -15.0 +_LOGSNR_MAX = 18.0 + + +def _logit_normal_schedule(u, mean, std): + # Reference time (0=noise..1=clean) via the probit/ndtri quantile. + u = torch.as_tensor(u, dtype=torch.float64) + t = 1.0 - torch.special.expit(mean + std * torch.special.ndtri(u)) + t_min = 1.0 / (1.0 + math.exp(0.5 * _LOGSNR_MAX)) + t_max = 1.0 / (1.0 + math.exp(0.5 * _LOGSNR_MIN)) + return t.clamp(t_min, t_max) + + +def ideogram4_sigmas(num_steps, width, height, mu, std): + """Descending sigmas (len num_steps+1) for the reference schedule. + + mu + the resolution term form the logSNR shift; std is the spread. + """ + mean = mu + 0.5 * math.log((width * height) / (512 * 512)) + u = torch.linspace(0.0, 1.0, num_steps + 1, dtype=torch.float64) + sigmas = (1.0 - _logit_normal_schedule(u, mean, std)).flip(0) + sigmas[-1] = 0.0 # clamp leaves ~6e-4; force full denoise + return sigmas.to(torch.float32) + + +class Ideogram4Scheduler(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id="Ideogram4Scheduler", + display_name="Ideogram 4 Scheduler", + category="sampling/custom_sampling/schedulers", + inputs=[ + io.Int.Input("steps", default=20, min=1, max=200), + io.Int.Input("width", default=1024, min=256, max=8192, step=16), + io.Int.Input("height", default=1024, min=256, max=8192, step=16), + io.Float.Input("mu", default=0.0, min=-10.0, max=10.0, step=0.05), + io.Float.Input("std", default=1.75, min=0.1, max=5.0, step=0.05), + ], + outputs=[io.Sigmas.Output()], + ) + + @classmethod + def execute(cls, steps, width, height, mu, std) -> io.NodeOutput: + return io.NodeOutput(ideogram4_sigmas(steps, width, height, mu, std)) + + +class Ideogram4Extension(ComfyExtension): + @override + async def get_node_list(self) -> list[type[io.ComfyNode]]: + return [Ideogram4Scheduler] + + +async def comfy_entrypoint() -> Ideogram4Extension: + return Ideogram4Extension() diff --git a/nodes.py b/nodes.py index 331425b87..2f5a478b5 100644 --- a/nodes.py +++ b/nodes.py @@ -969,7 +969,7 @@ class CLIPLoader: @classmethod def INPUT_TYPES(s): return {"required": { "clip_name": (folder_paths.get_filename_list("text_encoders"), ), - "type": (["stable_diffusion", "stable_cascade", "sd3", "stable_audio", "mochi", "ltxv", "pixart", "cosmos", "lumina2", "wan", "hidream", "chroma", "ace", "omnigen2", "qwen_image", "hunyuan_image", "flux2", "ovis", "longcat_image", "cogvideox", "lens", "pixeldit"], ), + "type": (["stable_diffusion", "stable_cascade", "sd3", "stable_audio", "mochi", "ltxv", "pixart", "cosmos", "lumina2", "wan", "hidream", "chroma", "ace", "omnigen2", "qwen_image", "hunyuan_image", "flux2", "ovis", "longcat_image", "cogvideox", "lens", "pixeldit", "ideogram4"], ), }, "optional": { "device": (["default", "cpu"], {"advanced": True}), @@ -2362,6 +2362,7 @@ async def init_builtin_extra_nodes(): "nodes_model_downscale.py", "nodes_images.py", "nodes_video_model.py", + "nodes_ideogram4.py", "nodes_train.py", "nodes_dataset.py", "nodes_sag.py", From f69225df245991dd0b1e212737805f48791d5cc3 Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Wed, 3 Jun 2026 08:55:18 -0700 Subject: [PATCH 07/21] Mark DualModelGuider as experimental (#14262) --- comfy_extras/nodes_custom_sampler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/comfy_extras/nodes_custom_sampler.py b/comfy_extras/nodes_custom_sampler.py index b790d7aac..2f4ff1f70 100644 --- a/comfy_extras/nodes_custom_sampler.py +++ b/comfy_extras/nodes_custom_sampler.py @@ -951,6 +951,7 @@ class DualModelGuider(io.ComfyNode): node_id="DualModelGuider", display_name="Dual Model CFG Guider", category="model/sampling/guiders", + is_experimental=True, inputs=[ io.Model.Input("model", tooltip="Model used for the positive (conditional) pass."), io.Model.Input("model_negative", optional=True, tooltip="Model used for the negative (unconditional) pass. Use the same model for ordinary CFG."), From f0619af65927bc0c42e4b35635c7fc8158566b43 Mon Sep 17 00:00:00 2001 From: "Daxiong (Lin)" Date: Thu, 4 Jun 2026 00:10:26 +0800 Subject: [PATCH 08/21] chore: update workflow templates to v0.9.94 (#14263) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7dff9e3c3..79d38fc06 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ comfyui-frontend-package==1.44.19 -comfyui-workflow-templates==0.9.92 +comfyui-workflow-templates==0.9.94 comfyui-embedded-docs==0.5.2 torch torchsde From 8e3045a90b4bed502ee06a9dd3032805579cccb9 Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Wed, 3 Jun 2026 09:19:18 -0700 Subject: [PATCH 09/21] Memory usage factor for ideogram 4 on non dynamic vram. (#14264) --- comfy/supported_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comfy/supported_models.py b/comfy/supported_models.py index 478489ed8..7cf9c133b 100644 --- a/comfy/supported_models.py +++ b/comfy/supported_models.py @@ -1757,7 +1757,7 @@ class Ideogram4(supported_models_base.BASE): "shift": 1.0, } - memory_usage_factor = 1.8 # TODO + memory_usage_factor = 11.6 unet_extra_config = { "num_attention_heads": 18, From f49bdb655707b97952dcef40e12e5af1f08d2007 Mon Sep 17 00:00:00 2001 From: comfyanonymous Date: Wed, 3 Jun 2026 12:42:13 -0400 Subject: [PATCH 10/21] ComfyUI v0.24.0 --- comfyui_version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/comfyui_version.py b/comfyui_version.py index 19e8f8cfc..4e3c924e6 100644 --- a/comfyui_version.py +++ b/comfyui_version.py @@ -1,3 +1,3 @@ # This file is automatically generated by the build process when version is # updated in pyproject.toml. -__version__ = "0.23.0" +__version__ = "0.24.0" diff --git a/pyproject.toml b/pyproject.toml index e118800e5..4107b4911 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ComfyUI" -version = "0.23.0" +version = "0.24.0" readme = "README.md" license = { file = "LICENSE" } requires-python = ">=3.10" From bb84c752831354d21f18488f5367497065c2151f Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Thu, 4 Jun 2026 05:20:30 +0900 Subject: [PATCH 11/21] chore(openapi): sync shared API contract from cloud@7c470f0 (#14174) --- openapi.yaml | 16661 +++++++++++++++---------------------------------- 1 file changed, 4934 insertions(+), 11727 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index f801a39d9..b7e21245f 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1,11749 +1,4956 @@ -openapi: 3.1.0 -info: - title: ComfyUI API - description: | - API for ComfyUI - A powerful and modular stable diffusion GUI and backend. - - This API allows you to interact with ComfyUI programmatically, including: - - Submitting and managing workflow executions - - Querying node/object information - - Uploading and viewing files - - Managing user settings and data - - Asset management (feature-gated) - - ## Dual-path routing - Every route registered via `self.routes` in the ComfyUI server is available at - both its bare path (e.g. `/prompt`) and an `/api`-prefixed path (e.g. `/api/prompt`). - This spec uses the `/api`-prefixed versions as canonical. - - ## Multi-user mode - When ComfyUI is started with `--multi-user`, the `Comfy-User` header identifies - the active user for settings, userdata, and history isolation. This is **not** a - security mechanism — it is an organisational convenience with no authentication - or authorisation behind it. - version: 1.0.0 - license: - name: GNU General Public License v3.0 - url: https://github.com/comfyanonymous/ComfyUI/blob/master/LICENSE - -servers: - - url: / - description: Default ComfyUI server (typically http://127.0.0.1:8188) - -tags: - - name: prompt - description: Workflow submission and prompt info - - name: queue - description: Queue inspection and management - - name: history - description: Execution history - - name: upload - description: File upload endpoints - - name: view - description: File viewing / download - - name: system - description: System stats and feature flags - - name: node - description: Node / object_info definitions - - name: model - description: Model folder and file listing - - name: user - description: User management (multi-user mode) - - name: userdata - description: Per-user file storage - - name: settings - description: Per-user settings - - name: extensions - description: Frontend extension JS files - - name: subgraph - description: Global subgraph blueprints - - name: internal - description: Internal / debug endpoints - - name: assets - description: Asset management (feature-gated behind enable-assets) - - - name: auth - description: Authentication and session management (cloud-only) - - name: billing - description: Billing, subscriptions, and payment management (cloud-only) - - name: workspace - description: Workspace and team management (cloud-only) - - name: hub - description: "ComfyUI Hub: profiles, shared workflows, and labels (cloud-only)" - - name: workflows - description: Cloud workflow management and versioning (cloud-only) - - name: task - description: Background task management (cloud-only) - - name: runtime-only - description: Operations served exclusively by the cloud runtime with no local equivalent - -paths: - # --------------------------------------------------------------------------- - # WebSocket - # --------------------------------------------------------------------------- - /ws: - get: - operationId: connectWebSocket - tags: [system] - summary: WebSocket connection for real-time updates - description: | - Upgrades to a WebSocket connection that streams execution progress, - node status, and output messages. The server sends an initial `status` - message with the session ID (SID) on connect. - - ## Message types (server → client) - The server sends JSON messages with a `type` field. See the - `x-websocket-messages` list below for the schema of each message type. - parameters: - - name: clientId - in: query - required: false - schema: - type: string - description: Client identifier. If omitted the server assigns one. - responses: - "101": - description: WebSocket upgrade successful - '401': - description: Unauthorized - x-websocket-messages: - - type: status - schema: - $ref: "#/components/schemas/StatusWsMessage" - - type: progress - schema: - $ref: "#/components/schemas/ProgressWsMessage" - - type: progress_text - schema: - $ref: "#/components/schemas/ProgressTextWsMessage" - - type: progress_state - schema: - $ref: "#/components/schemas/ProgressStateWsMessage" - - type: executing - schema: - $ref: "#/components/schemas/ExecutingWsMessage" - - type: executed - schema: - $ref: "#/components/schemas/ExecutedWsMessage" - - type: execution_start - schema: - $ref: "#/components/schemas/ExecutionStartWsMessage" - - type: execution_success - schema: - $ref: "#/components/schemas/ExecutionSuccessWsMessage" - - type: execution_cached - schema: - $ref: "#/components/schemas/ExecutionCachedWsMessage" - - type: execution_interrupted - schema: - $ref: "#/components/schemas/ExecutionInterruptedWsMessage" - - type: execution_error - schema: - $ref: "#/components/schemas/ExecutionErrorWsMessage" - - type: logs - schema: - $ref: "#/components/schemas/LogsWsMessage" - - type: notification - schema: - $ref: "#/components/schemas/NotificationWsMessage" - - type: feature_flags - schema: - $ref: "#/components/schemas/FeatureFlagsWsMessage" - - type: asset_download - schema: - $ref: "#/components/schemas/AssetDownloadWsMessage" - - type: asset_export - schema: - $ref: "#/components/schemas/AssetExportWsMessage" - - # --------------------------------------------------------------------------- - # Prompt - # --------------------------------------------------------------------------- - /api/prompt: - get: - operationId: getPromptInfo - tags: [prompt] - summary: Get queue status - description: Returns how many items remain in the execution queue. - responses: - "200": - description: Queue info - content: - application/json: - schema: - $ref: "#/components/schemas/PromptInfo" - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: executePrompt - tags: [prompt] - summary: Submit a workflow for execution - description: Submits a workflow for execution. The server validates the graph, assigns a `prompt_id`, and enqueues it. Clients listen on `/ws` for execution progress and output messages. - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PromptRequest" - responses: - "200": - description: Prompt accepted - content: - application/json: - schema: - $ref: "#/components/schemas/PromptResponse" - "400": - description: Validation or node errors - content: - application/json: - schema: - $ref: "#/components/schemas/PromptErrorResponse" - - '402': - description: Payment required - Insufficient credits - content: - application/json: - schema: - $ref: '#/components/schemas/PromptErrorResponse' - '429': - description: Payment required - User has not paid - content: - application/json: - schema: - $ref: '#/components/schemas/PromptErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/PromptErrorResponse' - '503': - description: Service unavailable - content: - application/json: - schema: - $ref: '#/components/schemas/PromptErrorResponse' - # --------------------------------------------------------------------------- - # Queue - # --------------------------------------------------------------------------- - /api/queue: - get: - operationId: getQueueInfo - tags: [queue] - summary: Get running and pending queue items - description: Returns the server's current execution queue, split into the currently-running prompt and the list of pending prompts. - responses: - "200": - description: Queue contents - content: - application/json: - schema: - $ref: "#/components/schemas/QueueInfo" - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: manageQueue - tags: [queue] - summary: Clear or delete items from the queue - description: Mutates the execution queue. Supports clearing all queued prompts or deleting individual prompts by ID. - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/QueueManageRequest" - responses: - "200": - description: Queue updated - content: - application/json: - schema: - $ref: "#/components/schemas/QueueManageResponse" - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/interrupt: - post: - operationId: interruptJob - tags: [queue] - summary: Interrupt current execution - description: Interrupts the prompt that is currently executing. The next queued prompt (if any) will start immediately after. - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - prompt_id: - type: string - format: uuid - description: "If provided, only interrupts this specific running prompt. Otherwise interrupts all." - responses: - "200": - description: Interrupt signal sent - - '401': - description: Unauthorized - Authentication required - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/free: - post: - operationId: freeMemory - tags: [queue] - summary: Free GPU memory and/or unload models - description: Frees GPU memory by unloading models and/or freeing the resident model cache, controlled by the request flags. - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - unload_models: - type: boolean - description: Unload all models from VRAM/RAM - free_memory: - type: boolean - description: Run garbage collection and free cached memory - responses: - "200": - description: Memory freed - - # --------------------------------------------------------------------------- - # Jobs - # --------------------------------------------------------------------------- - /api/jobs: - get: - operationId: listJobs - tags: [queue] - summary: List jobs with filtering and pagination - description: Returns a paginated list of completed prompt executions, newest first. - parameters: - - name: status - in: query - schema: - type: string - description: Filter by job status - - name: workflow_id - in: query - schema: - type: string - description: Filter by workflow ID - - name: sort_by - in: query - schema: - type: string - description: Field to sort by - - name: sort_order - in: query - schema: - type: string - enum: [asc, desc] - description: Sort direction - - name: limit - in: query - schema: - type: integer - description: Maximum number of results (default is unlimited/None) - - name: offset - in: query - schema: - type: integer - default: 0 - description: Pagination offset - responses: - "200": - description: Jobs list - content: - application/json: - schema: - type: object - properties: - jobs: - type: array - items: - $ref: "#/components/schemas/JobEntry" - pagination: - $ref: "#/components/schemas/PaginationInfo" - - '401': - description: Unauthorized - Authentication required - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/jobs/{job_id}: - get: - operationId: getJobDetail - tags: [queue] - summary: Get a single job by ID - description: Returns the full record for a single completed prompt execution, including its outputs, status, and metadata. - parameters: - - name: job_id - in: path - description: The job (prompt) ID to fetch. - required: true - schema: - type: string - format: uuid - responses: - "200": - description: Job detail - content: - application/json: - schema: - $ref: "#/components/schemas/JobDetailResponse" - "404": - description: Job not found - - '401': - description: Unauthorized - Authentication required - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '403': - description: Forbidden - Job does not belong to user - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # History - # --------------------------------------------------------------------------- - /api/history: - get: - operationId: getPromptHistory - tags: [history] - summary: Get execution history - deprecated: true - description: | - **Deprecated.** Superseded by `GET /api/jobs`, which returns the same - execution records in a paginated, filterable format. Planned for removal - no earlier than a future major release; sunset timeline TBD. - - Returns a dictionary keyed by prompt_id. Each value is a HistoryEntry - containing prompt metadata, outputs, status, and node meta. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: max_items - in: query - schema: - type: integer - description: Maximum number of history entries to return - - name: offset - in: query - schema: - type: integer - description: Pagination offset (number of entries to skip) - responses: - "200": - description: History dictionary keyed by prompt_id - content: - application/json: - schema: - type: object - additionalProperties: - $ref: "#/components/schemas/HistoryEntry" - '404': - description: "Not Found \u2014 use /api/history_v2 instead" - post: - operationId: manageHistory - tags: [history] - summary: Clear or delete history entries - deprecated: true - description: | - **Deprecated.** Superseded by the forthcoming job-management endpoints - under `/api/jobs`. Planned for removal no earlier than a future major - release; sunset timeline TBD. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/HistoryManageRequest" - responses: - "200": - description: History updated - - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - Authentication required - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/history/{prompt_id}: - get: - operationId: getHistoryByPromptId - tags: [history] - summary: Get history for a specific prompt - deprecated: true - description: | - **Deprecated.** Superseded by `GET /api/jobs/{job_id}`, which returns - the same execution record. Planned for removal no earlier than a future - major release; sunset timeline TBD. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: prompt_id - in: path - description: The prompt ID to fetch history for. - required: true - schema: - type: string - format: uuid - responses: - "200": - description: Single-entry history dictionary. Returns an empty object `{}` if the prompt_id is not found. - content: - application/json: - schema: - type: object - additionalProperties: - $ref: "#/components/schemas/HistoryEntry" - - '404': - description: "Not Found \u2014 use /api/jobs/{prompt_id} instead" - # --------------------------------------------------------------------------- - # Upload - # --------------------------------------------------------------------------- - /api/upload/image: - post: - operationId: uploadImage - tags: [upload] - summary: Upload an image file - description: Uploads an image file into one of the input/output/temp directories so it can be referenced by workflow nodes. - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - image - properties: - image: - type: string - format: binary - description: Image file to upload - type: - type: string - enum: [input, temp, output] - default: input - description: Target directory type - overwrite: - type: string - description: 'Set to "true" to overwrite existing files' - subfolder: - type: string - description: Subfolder within the target directory - responses: - "200": - description: Upload result - content: - application/json: - schema: - $ref: "#/components/schemas/UploadResult" - "400": - description: No file provided or invalid request - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/upload/mask: - post: - operationId: uploadMask - tags: [upload] - deprecated: true - summary: Upload a mask image (deprecated) - description: | - Deprecated. Clients should composite the mask onto the source image - client-side and upload the resulting image via POST /api/upload/image - instead. This endpoint will continue to function for older clients, - but will not receive new features. - - Uploads a mask image associated with a previously-uploaded reference image. - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - image - - original_ref - properties: - image: - type: string - format: binary - description: Mask image (alpha channel is used) - original_ref: - type: object - description: Reference to the original image file - required: - - filename - properties: - filename: - type: string - description: Filename of the original image - additionalProperties: true - type: - type: string - enum: [input, temp, output] - default: input - description: Target directory type - overwrite: - type: string - description: 'Set to "true" to overwrite existing files' - subfolder: - type: string - description: Subfolder within the target directory - responses: - "200": - description: Upload result - content: - application/json: - schema: - $ref: "#/components/schemas/UploadResult" - "400": - description: No file provided or invalid request - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # View - # --------------------------------------------------------------------------- - /api/view: - get: - operationId: viewFile - tags: [view] - summary: View or download a file - description: Serves a file (image, audio, or video) from the input/output/temp directory identified by the query parameters. - parameters: - - name: filename - in: query - required: true - schema: - type: string - description: Name of the file to view - - name: type - in: query - schema: - type: string - enum: [input, output, temp] - default: output - description: Directory type - - name: subfolder - in: query - schema: - type: string - description: Subfolder within the directory - - name: preview - in: query - schema: - type: string - description: Preview format hint (e.g. "webp;90") - - name: channel - in: query - schema: - type: string - enum: [rgba, rgb, a] - description: Channel extraction mode - responses: - "200": - description: File content - content: - image/*: - schema: - type: string - format: binary - video/*: - schema: - type: string - format: binary - audio/*: - schema: - type: string - format: binary - application/octet-stream: - schema: - type: string - format: binary - "404": - description: File not found - - '302': - description: Redirect to GCS signed URL - headers: - Location: - description: Signed URL to access the file in GCS - schema: - type: string - Cache-Control: - description: Cache directive for the redirect response - schema: - type: string - Vary: - description: Headers that affect response caching - schema: - type: string - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/view_metadata/{folder_name}: - get: - operationId: viewMetadata - tags: [view] - summary: Get metadata for a file (e.g. safetensors header) - description: Returns embedded metadata parsed from a file in the given folder — for example, the header of a safetensors model. - parameters: - - name: folder_name - in: path - required: true - schema: - type: string - description: Folder type (output, input, temp, etc.) - - name: filename - in: query - required: true - schema: - type: string - description: Filename to read metadata from - responses: - "200": - description: File metadata - content: - application/json: - schema: - type: object - additionalProperties: true - "404": - description: File or metadata not found - - # --------------------------------------------------------------------------- - # System - # --------------------------------------------------------------------------- - /api/system_stats: - get: - operationId: getSystemStats - tags: [system] - summary: Get system statistics - description: Returns hardware, Python, VRAM, and runtime statistics for the running ComfyUI process. - responses: - "200": - description: System stats - content: - application/json: - schema: - $ref: "#/components/schemas/SystemStatsResponse" - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/features: - get: - operationId: getFeatures - tags: [system] - summary: Get enabled feature flags - description: Returns a dictionary of feature flag names to their enabled state. Cloud deployments may include additional typed fields alongside the boolean flags. - responses: - "200": - description: Feature flags - content: - application/json: - schema: - type: object - additionalProperties: - type: boolean - properties: - max_upload_size: - type: integer - format: int64 - minimum: 0 - description: "Maximum file upload size in bytes." - free_tier_credits: - type: integer - format: int32 - minimum: 0 - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Credits available to free-tier users. Local ComfyUI returns null." - posthog_api_host: - type: string - format: uri - nullable: true - x-runtime: [cloud] - description: "[cloud-only] PostHog analytics proxy URL for frontend telemetry. Local ComfyUI returns null." - max_concurrent_jobs: - type: integer - format: int32 - minimum: 0 - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Maximum concurrent jobs the authenticated user can run. Local ComfyUI returns null." - workflow_templates_version: - type: string - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Version identifier for the workflow templates bundle. Local ComfyUI returns null." - workflow_templates_source: - type: string - nullable: true - enum: [dynamic_config_override, workflow_templates_version_json] - x-runtime: [cloud] - description: "[cloud-only] How the templates version was resolved. Local ComfyUI returns null." - - # --------------------------------------------------------------------------- - # Node / Object Info - # --------------------------------------------------------------------------- - /api/object_info: - get: - operationId: getNodeInfo - tags: [node] - summary: Get all node definitions - description: | - Returns a dictionary of every registered node class, keyed by class name. - Each value is a NodeInfo object describing inputs, outputs, category, etc. - responses: - "200": - description: All node definitions - content: - application/json: - schema: - type: object - additionalProperties: - $ref: "#/components/schemas/NodeInfo" - - /api/object_info/{node_class}: - get: - operationId: getObjectInfoByClass - tags: [node] - summary: Get a single node definition - description: Returns the `NodeInfo` definition for a single registered node class. - parameters: - - name: node_class - in: path - required: true - schema: - type: string - description: Node class name (e.g. "KSampler") - responses: - "200": - description: Single node definition - content: - application/json: - schema: - type: object - additionalProperties: - $ref: "#/components/schemas/NodeInfo" - "404": - description: Node class not found - - /api/embeddings: - get: - operationId: getEmbeddings - tags: [node] - summary: List available embedding names - description: Returns the list of text-encoder embeddings available on disk. - responses: - "200": - description: Embedding names - content: - application/json: - schema: - type: array - items: - type: string - - # --------------------------------------------------------------------------- - # Models - # --------------------------------------------------------------------------- - /api/models: - get: - operationId: getModelTypes - tags: [model] - summary: List model folder type names - description: Returns an array of model type names (e.g. checkpoints, loras, vae). - responses: - "200": - description: Model type names - content: - application/json: - schema: - type: array - items: - type: string - - '404': - description: "Not Found \u2014 use /api/experiment/models instead" - /api/models/{folder}: - get: - operationId: getModelsByFolder - tags: [model] - summary: List model filenames in a folder - description: Returns the names of model files in the given folder. This endpoint predates `/api/experiment/models/{folder}` and returns names only — prefer the experiment endpoint for new integrations. - parameters: - - name: folder - in: path - required: true - schema: - type: string - description: Model folder type name - responses: - "200": - description: Model filenames - content: - application/json: - schema: - type: array - items: - type: string - "404": - description: Unknown folder type - - /api/experiment/models: - get: - operationId: getModelFolders - tags: [model] - summary: List model folders with paths - description: Returns an array of model folder objects with name and folder paths. - responses: - "200": - description: Model folders - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ModelFolder" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/experiment/models/{folder}: - get: - operationId: getModelsInFolder - tags: [model] - summary: List model files with metadata - description: Returns the model files in the given folder with richer metadata (path index, mtime, size) than the legacy `/api/models/{folder}` endpoint. - parameters: - - name: folder - in: path - required: true - schema: - type: string - description: Model folder type name - responses: - "200": - description: Model files with metadata - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/ModelFile" - "404": - description: Unknown folder type - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/experiment/models/preview/{folder}/{path_index}/{filename}: - get: - operationId: getModelPreview - tags: [model] - summary: Get model preview image - description: Returns the preview image associated with a model file, if one exists alongside the model on disk. - parameters: - - name: folder - in: path - required: true - schema: - type: string - description: Model folder type name - - name: path_index - in: path - required: true - schema: - type: integer - description: Path index within the folder - - name: filename - in: path - required: true - schema: - type: string - description: Model filename - responses: - "200": - description: Preview image (WebP) - content: - image/webp: - schema: - type: string - format: binary - "404": - description: Preview not found - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Users - # --------------------------------------------------------------------------- - /api/users: - get: - operationId: getUsersInfo - tags: [user] - summary: Get user storage info - description: | - Returns user storage configuration. In single-user mode returns - `{"storage": "server", "migrated": true/false}`. In multi-user mode - returns `{"storage": "server", "users": {"user_id": "user_dir", ...}}`. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - responses: - "200": - description: User info - content: - application/json: - schema: - type: object - properties: - storage: - type: string - description: Storage backend type (always "server") - migrated: - type: boolean - description: Whether migration from browser storage is complete (single-user) - users: - type: object - additionalProperties: - type: string - description: Map of user_id to directory name (multi-user) - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: createUser - tags: [user] - summary: Create a new user (multi-user mode) - description: Creates a new user entry. Only meaningful when ComfyUI is running in multi-user mode. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - username - properties: - username: - type: string - description: Username for the new user - responses: - "200": - description: Created user ID - content: - application/json: - schema: - type: string - description: The generated user_id - "400": - description: Username already exists or invalid - - # --------------------------------------------------------------------------- - # Userdata - # --------------------------------------------------------------------------- - /api/userdata: - get: - operationId: getUserdata - tags: [userdata] - summary: List files in a userdata directory - description: Lists files in the authenticated user's data directory. Returns either filename strings or full objects depending on the `full_info` query parameter. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: dir - in: query - required: true - schema: - type: string - description: Directory path relative to the user's data folder - - name: recurse - in: query - schema: - type: boolean - description: Recurse into subdirectories - - name: full_info - in: query - schema: - type: boolean - description: Return full file info objects instead of just names - - name: split - in: query - schema: - type: boolean - description: Split paths into directory components - responses: - "200": - description: File listing - content: - application/json: - schema: - $ref: "#/components/schemas/GetUserDataResponseFull" - "404": - description: Directory not found - - '400': - description: Bad request (e.g., invalid filename). - content: - text/plain: - schema: - type: string - '401': - description: Unauthorized. - content: - text/plain: - schema: - type: string - '500': - description: General error - content: - text/plain: - schema: - type: string - /api/v2/userdata: - get: - operationId: listUserdataV2 - tags: [userdata] - summary: List files in userdata (v2 format) - description: Lists files in the authenticated user's data directory using the v2 response shape, which always returns full objects. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: path - in: query - schema: - type: string - description: Directory path relative to user data root - responses: - "200": - description: File listing with metadata - content: - application/json: - schema: - type: array - items: - type: object - properties: - name: - type: string - path: - type: string - type: - type: string - enum: [file, directory] - size: - type: integer - modified: - type: number - description: Unix timestamp - - '404': - description: "Not Found \u2014 use /api/userdata instead" - /api/userdata/{file}: - get: - operationId: getUserdataFile - tags: [userdata] - summary: Read a userdata file - description: Reads the contents of a file from the authenticated user's data directory. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: file - in: path - required: true - schema: - type: string - description: File path relative to user data directory - responses: - "200": - description: File content - content: - application/octet-stream: - schema: - type: string - format: binary - "404": - description: File not found - '400': - description: Bad request (e.g., invalid filename). - content: - text/plain: - schema: - type: string - '401': - description: Unauthorized. - content: - text/plain: - schema: - type: string - '500': - description: General error - content: - text/plain: - schema: - type: string - post: - operationId: postUserdataFile - tags: [userdata] - summary: Write or create a userdata file - description: Writes (creates or replaces) a file in the authenticated user's data directory. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: file - in: path - required: true - schema: - type: string - description: File path relative to user data directory - - name: overwrite - in: query - schema: - type: boolean - description: Allow overwriting existing files - - name: full_info - in: query - schema: - type: boolean - description: Return full file info in response - requestBody: - required: true - content: - application/octet-stream: - schema: - type: string - format: binary - application/json: - schema: {} - responses: - "200": - description: File written - content: - application/json: - schema: - $ref: "#/components/schemas/UserDataResponseFull" - "409": - description: File exists and overwrite not set - '400': - description: Missing or invalid 'file' parameter. - content: - text/plain: - schema: - type: string - '401': - description: Unauthorized. - content: - text/plain: - schema: - type: string - '403': - description: The requested path is not allowed. - content: - text/plain: - schema: - type: string - '500': - description: General error - content: - text/plain: - schema: - type: string - delete: - operationId: deleteUserdataFile - tags: [userdata] - summary: Delete a userdata file - description: Deletes a file from the authenticated user's data directory. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: file - in: path - required: true - schema: - type: string - description: File path relative to user data directory - responses: - "204": - description: File deleted - "404": - description: File not found - - '401': - description: Unauthorized. - content: - text/plain: - schema: - type: string - '500': - description: Internal server error. - content: - text/plain: - schema: - type: string - /api/userdata/{file}/move/{dest}: - post: - operationId: moveUserdataFile - tags: [userdata] - summary: Move or rename a userdata file - description: Renames or moves a file within the authenticated user's data directory. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: file - in: path - required: true - schema: - type: string - description: Source file path - - name: dest - in: path - required: true - schema: - type: string - description: Destination file path - - name: overwrite - in: query - schema: - type: boolean - description: Allow overwriting at destination - - name: full_info - in: query - schema: - type: boolean - description: Return full file info in response - responses: - "200": - description: File moved - content: - application/json: - schema: - $ref: "#/components/schemas/UserDataResponseFull" - "404": - description: Source file not found - "409": - description: Destination exists and overwrite not set - - '400': - description: Missing or invalid parameters. - content: - text/plain: - schema: - type: string - '401': - description: Unauthorized. - content: - text/plain: - schema: - type: string - '500': - description: General error - content: - text/plain: - schema: - type: string - # --------------------------------------------------------------------------- - # Settings - # --------------------------------------------------------------------------- - /api/settings: - get: - operationId: getAllSettings - tags: [settings] - summary: Get all user settings - description: Returns all settings for the authenticated user. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - responses: - "200": - description: Settings object - content: - application/json: - schema: - type: object - additionalProperties: true - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: updateMultipleSettings - tags: [settings] - summary: Update user settings (partial merge) - description: Replaces the authenticated user's settings with the provided object. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - requestBody: - required: true - content: - application/json: - schema: - type: object - additionalProperties: true - description: Partial settings to merge - responses: - "200": - description: Settings updated - - '400': - description: Invalid request - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/settings/{id}: - get: - operationId: getSettingById - tags: [settings] - summary: Get a single setting by key - description: Returns the value of a single setting, identified by key. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: id - in: path - required: true - schema: - type: string - description: Setting key - responses: - "200": - description: Setting value (null if the setting does not exist) - content: - application/json: - schema: - nullable: true - description: The setting value (any JSON type), or null if not set - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Setting not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: updateSettingById - tags: [settings] - summary: Set a single setting value - description: Sets the value of a single setting, identified by key. - parameters: - - $ref: "#/components/parameters/ComfyUserHeader" - - name: id - in: path - required: true - schema: - type: string - description: Setting key - requestBody: - required: true - content: - application/json: - schema: - description: The setting value (any JSON type) - responses: - "200": - description: Setting updated - - '400': - description: Invalid request - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Extensions / Templates / i18n - # --------------------------------------------------------------------------- - /api/extensions: - get: - operationId: getExtensions - tags: [extensions] - summary: List frontend extension JS file paths - description: Returns the list of frontend extension JS URLs registered by custom nodes, to be loaded by the frontend on startup. - responses: - "200": - description: Array of JS file paths - content: - application/json: - schema: - type: array - items: - type: string - description: Relative path to extension JS file - - /api/workflow_templates: - get: - operationId: getWorkflowTemplates - tags: [extensions] - summary: Get workflow template mappings - description: Returns a map of custom node names to their provided workflow template names. - responses: - "200": - description: Template mappings - content: - application/json: - schema: - type: object - additionalProperties: - type: array - items: - type: string - description: Map of node pack name to array of template names - - /api/i18n: - get: - operationId: getI18n - tags: [extensions] - summary: Get internationalisation translation strings - description: Returns the URLs of translation files contributed by custom nodes, keyed by locale. - responses: - "200": - description: Translation map - content: - application/json: - schema: - type: object - additionalProperties: true - description: Nested map of locale to translation key-value pairs - - # --------------------------------------------------------------------------- - # Subgraphs - # --------------------------------------------------------------------------- - /api/global_subgraphs: - get: - operationId: getGlobalSubgraphs - tags: [subgraph] - summary: List global subgraph blueprints - description: Returns a dictionary of subgraph IDs to their metadata. - responses: - "200": - description: Subgraph metadata dictionary - content: - application/json: - schema: - type: object - additionalProperties: - $ref: "#/components/schemas/GlobalSubgraphInfo" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/global_subgraphs/{id}: - get: - operationId: getGlobalSubgraph - tags: [subgraph] - summary: Get a global subgraph with full data - description: Returns the blueprint for a globally-registered subgraph, used by the frontend to materialize the subgraph node. - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Subgraph identifier - responses: - "200": - description: Full subgraph data - content: - application/json: - schema: - $ref: "#/components/schemas/GlobalSubgraphData" - "404": - description: Subgraph not found - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Node Replacements - # --------------------------------------------------------------------------- - /api/node_replacements: - get: - operationId: getNodeReplacements - tags: [node] - summary: Get node replacement mappings - description: | - Returns a dictionary mapping deprecated or replaced node class names - to their replacement node information. - responses: - "200": - description: Replacement mappings - content: - application/json: - schema: - type: object - additionalProperties: true - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Internal (x-internal: true) - # --------------------------------------------------------------------------- - /internal/logs: - get: - operationId: getInternalLogs - tags: [internal] - summary: Get server logs as text - description: Returns structured ComfyUI log entries from the in-memory log buffer. - x-internal: true - responses: - "200": - description: Log text - content: - text/plain: - schema: - type: string - - /internal/logs/raw: - get: - operationId: getInternalLogsRaw - tags: [internal] - summary: Get raw structured log entries - description: Returns the raw ComfyUI log buffer as text, together with metadata about the current size limit. - x-internal: true - responses: - "200": - description: Structured log data - content: - application/json: - schema: - type: object - properties: - entries: - type: array - items: - type: object - properties: - t: - type: number - description: Timestamp - m: - type: string - description: Message - size: - type: object - properties: - cols: - type: integer - rows: - type: integer - - /internal/logs/subscribe: - patch: - operationId: subscribeToLogs - tags: [internal] - summary: Subscribe or unsubscribe a WebSocket client to log streaming - description: Subscribes or unsubscribes the current client from live log streaming over the WebSocket. - x-internal: true - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - clientId - - enabled - properties: - clientId: - type: string - description: WebSocket client ID - enabled: - type: boolean - description: Enable or disable log streaming for this client - responses: - "200": - description: Subscription updated - - /internal/folder_paths: - get: - operationId: getInternalFolderPaths - tags: [internal] - summary: Get configured folder paths - description: Returns the filesystem paths ComfyUI is configured to load models and other assets from, keyed by folder type. - x-internal: true - responses: - "200": - description: Dictionary of folder type to paths - content: - application/json: - schema: - type: object - additionalProperties: - type: array - items: - type: array - items: - type: string - description: Map of folder type name to list of [path, ...] entries - - /internal/files/{directory_type}: - get: - operationId: getFiles - tags: [internal] - summary: List files in a directory type - description: Lists the files present in one of ComfyUI's known directories (input, output, or temp). - x-internal: true - parameters: - - name: directory_type - in: path - required: true - schema: - type: string - description: Directory type (e.g. output, input, temp) - responses: - "200": - description: Array of filenames - content: - application/json: - schema: - type: array - items: - type: string - - '400': - description: Invalid directory type - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Assets (x-feature-gate: enable-assets) - # --------------------------------------------------------------------------- - /api/assets/hash/{hash}: - head: - operationId: checkAssetByHash - tags: [assets] - summary: Check if an asset with the given hash exists - description: Returns 204 if an asset with the given content hash already exists, 404 otherwise. Used by clients to deduplicate uploads before transferring bytes. - x-feature-gate: enable-assets - parameters: - - name: hash - in: path - required: true - schema: - type: string - description: "Blake3 hash of the asset (e.g. blake3:abc123...)" - responses: - "200": - description: Asset exists - "404": - description: No asset with this hash - - '400': - description: Invalid hash format - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets: - get: - operationId: listAssets - tags: [assets] - summary: List assets with filtering and pagination - description: Returns a paginated list of assets, optionally filtered by tags, name, or other query parameters. - x-feature-gate: enable-assets - parameters: - - name: limit - in: query - schema: - type: integer - default: 50 - - name: offset - in: query - schema: - type: integer - default: 0 - - name: include_tags - in: query - schema: - type: array - items: - type: string - style: form - explode: true - description: Tags that assets must have (AND logic) - - name: exclude_tags - in: query - schema: - type: array - items: - type: string - style: form - explode: true - description: Tags that assets must not have - - name: name_contains - in: query - schema: - type: string - description: Filter assets whose name contains this substring - - name: metadata_filter - in: query - schema: - type: string - description: JSON-encoded metadata key/value filter - - name: sort - in: query - schema: - type: string - description: Field to sort by - - name: order - in: query - schema: - type: string - enum: [asc, desc] - description: Sort direction - - name: include_public - in: query - schema: - type: boolean - x-runtime: [cloud] - description: "[cloud-only] Include workspace-public assets in addition to the caller's own." - - name: asset_hash - in: query - schema: - type: string - x-runtime: [cloud] - description: "[cloud-only] Filter by exact content hash." - responses: - "200": - description: Asset list - content: - application/json: - schema: - $ref: "#/components/schemas/ListAssetsResponse" - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: uploadAsset - tags: [assets] - summary: Upload a new asset - description: Uploads a new asset (binary content plus metadata) and registers it in the asset database. - x-feature-gate: enable-assets - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: - - file - properties: - file: - type: string - format: binary - description: Asset file to upload - name: - type: string - description: Display name for the asset - tags: - type: string - description: Comma-separated tags - user_metadata: - type: string - description: JSON-encoded user metadata - hash: - type: string - description: "Blake3 hash of the file content (e.g. blake3:abc123...)" - mime_type: - type: string - description: MIME type of the file (overrides auto-detected type) - preview_id: - type: string - format: uuid - description: ID of an existing asset to use as the preview image - id: - type: string - format: uuid - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Client-supplied asset ID for idempotent creation. If an asset with this ID already exists, the existing asset is returned." - application/json: - schema: - type: object - x-runtime: [cloud] - description: "[cloud-only] URL-based asset upload. Caller supplies a URL instead of a file body; the server fetches the content." - required: - - url - properties: - url: - type: string - format: uri - description: "[cloud-only] URL of the file to import as an asset" - name: - type: string - description: Display name for the asset - tags: - type: string - description: Comma-separated tags - user_metadata: - type: string - description: JSON-encoded user metadata - hash: - type: string - description: "Blake3 hash of the file content (e.g. blake3:abc123...)" - mime_type: - type: string - description: MIME type of the file (overrides auto-detected type) - preview_id: - type: string - format: uuid - description: ID of an existing asset to use as the preview image - id: - type: string - format: uuid - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Client-supplied asset ID for idempotent creation. If an asset with this ID already exists, the existing asset is returned." - responses: - "201": - description: Asset created - content: - application/json: - schema: - $ref: "#/components/schemas/AssetCreated" - - '200': - description: Asset already exists (returned existing asset) - content: - application/json: - schema: - $ref: '#/components/schemas/AssetCreated' - '400': - description: Invalid request (bad file, invalid URL, invalid content type, etc.) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '403': - description: Source URL requires authentication or access denied - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Source URL not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '413': - description: File too large - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '415': - description: Unsupported media type - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Download failed due to network error or timeout - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/from-hash: - post: - operationId: createAssetFromHash - tags: [assets] - summary: Create an asset reference from an existing hash - description: Registers a new asset that references existing content by hash, without re-uploading the bytes. - x-feature-gate: enable-assets - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - hash - - name - properties: - hash: - type: string - description: Blake3 hash of existing content - name: - type: string - description: Display name - tags: - type: array - items: - type: string - user_metadata: - type: object - additionalProperties: true - mime_type: - type: string - nullable: true - x-runtime: [cloud] - description: "[cloud-only] MIME type of the content, so the type is preserved without re-inspecting content. Ignored by local ComfyUI." - responses: - "201": - description: Asset created from hash - content: - application/json: - schema: - $ref: "#/components/schemas/AssetCreated" - - '200': - description: Asset reference already exists (returned existing) - content: - application/json: - schema: - $ref: '#/components/schemas/AssetCreated' - '400': - description: Invalid request (bad hash format, invalid tags, etc.) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Source asset with given hash not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/{id}: - get: - operationId: getAssetById - tags: [assets] - summary: Get asset metadata - description: Returns the metadata for a single asset. - x-feature-gate: enable-assets - parameters: - - name: id - in: path - description: The asset ID. - required: true - schema: - type: string - format: uuid - responses: - "200": - description: Asset metadata - content: - application/json: - schema: - $ref: "#/components/schemas/Asset" - "404": - description: Asset not found - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - put: - operationId: updateAsset - tags: [assets] - summary: Update asset metadata - description: Updates the mutable metadata of an asset (name, tags, etc.). Binary content is immutable. - x-feature-gate: enable-assets - parameters: - - name: id - in: path - description: The asset ID. - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: New display name for the asset - user_metadata: - type: object - additionalProperties: true - description: Custom user metadata to set - preview_id: - type: string - format: uuid - description: ID of the asset to use as the preview - mime_type: - type: string - nullable: true - x-runtime: [cloud] - description: "[cloud-only] MIME type override when auto-detection was wrong. Ignored by local ComfyUI." - responses: - "200": - description: Asset updated - content: - application/json: - schema: - $ref: "#/components/schemas/AssetUpdated" - '400': - description: Invalid request (no fields provided) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Asset not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: deleteAsset - tags: [assets] - summary: Delete an asset - description: Removes an asset entry. Depending on the server configuration, the underlying content may also be deleted. - x-feature-gate: enable-assets - parameters: - - name: id - in: path - description: The asset ID. - required: true - schema: - type: string - format: uuid - - name: delete_content - in: query - schema: - type: boolean - description: Also delete the underlying content file - responses: - "204": - description: Asset deleted - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Asset not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '409': - description: Asset cannot be deleted because it is referenced by another resource (e.g., workflow version) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/{id}/content: - get: - operationId: getAssetContent - tags: [assets] - summary: Download asset file content - description: Returns the binary content of an asset. Supports range requests. - x-feature-gate: enable-assets - parameters: - - name: id - in: path - description: The asset ID. - required: true - schema: - type: string - format: uuid - responses: - "200": - description: Asset file content - content: - application/octet-stream: - schema: - type: string - format: binary - "404": - description: Asset not found - - /api/assets/{id}/tags: - post: - operationId: addAssetTags - tags: [assets] - summary: Add tags to an asset - description: Adds one or more tags to an asset. - x-feature-gate: enable-assets - parameters: - - name: id - in: path - description: The asset ID. - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - tags - properties: - tags: - type: array - items: - type: string - responses: - "200": - description: Tags added - content: - application/json: - schema: - $ref: "#/components/schemas/TagsModificationResponse" - '400': - description: Invalid request - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Asset not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error (e.g., reserved tag) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: removeAssetTags - tags: [assets] - summary: Remove tags from an asset - description: Removes one or more tags from an asset. - x-feature-gate: enable-assets - parameters: - - name: id - in: path - description: The asset ID. - required: true - schema: - type: string - format: uuid - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - tags - properties: - tags: - type: array - items: - type: string - responses: - "200": - description: Tags removed - content: - application/json: - schema: - $ref: "#/components/schemas/TagsModificationResponse" - - '400': - description: Invalid request - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Asset not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error (e.g., reserved tag) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/tags: - get: - operationId: listTags - tags: [assets] - summary: List all known tags with counts - description: Returns the list of all tags known to the asset database, with counts. - x-feature-gate: enable-assets - parameters: - - name: limit - in: query - schema: - type: integer - - name: offset - in: query - schema: - type: integer - - name: search - in: query - schema: - type: string - description: Search term for tag name - responses: - "200": - description: Tag list - content: - application/json: - schema: - $ref: "#/components/schemas/ListTagsResponse" - - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/tags/refine: - get: - operationId: getAssetTagHistogram - tags: [assets] - summary: Get tag counts for assets matching current filters - description: Returns suggested additional tags that would refine a filtered asset query, together with the count of assets each tag would select. - x-feature-gate: enable-assets - parameters: - - name: include_tags - in: query - schema: - type: array - items: - type: string - style: form - explode: true - description: Tags that assets must have (AND logic) - - name: exclude_tags - in: query - schema: - type: array - items: - type: string - style: form - explode: true - description: Tags that assets must not have - - name: name_contains - in: query - schema: - type: string - description: Filter assets whose name contains this substring - - name: metadata_filter - in: query - schema: - type: string - description: JSON-encoded metadata key/value filter - - name: limit - in: query - schema: - type: integer - - name: offset - in: query - schema: - type: integer - - name: sort - in: query - schema: - type: string - description: Field to sort by - - name: order - in: query - schema: - type: string - enum: [asc, desc] - description: Sort direction - responses: - "200": - description: Tag histogram - content: - application/json: - schema: - $ref: "#/components/schemas/AssetTagHistogramResponse" - - '400': - description: Invalid request parameters - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/seed: - post: - operationId: seedAssets - tags: [assets] - summary: Trigger asset scan/seed from filesystem - description: Starts a background job that scans the configured directories and registers any assets not yet present in the asset database. - x-feature-gate: enable-assets - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - roots: - type: array - items: - type: string - description: Root folder paths to scan (if omitted, scans all) - responses: - "200": - description: Seed started - content: - application/json: - schema: - type: object - properties: - status: - type: string - - /api/assets/seed/status: - get: - operationId: getAssetSeedStatus - tags: [assets] - summary: Get asset scan progress - description: Returns the progress and status of the most recently-started asset seed job. - x-feature-gate: enable-assets - responses: - "200": - description: Scan progress - content: - application/json: - schema: - type: object - additionalProperties: true - description: Scan progress details (files scanned, total, status, etc.) - - /api/assets/seed/cancel: - post: - operationId: cancelAssetSeed - tags: [assets] - summary: Cancel an in-progress asset scan - description: Requests cancellation of the currently-running asset seed job. - x-feature-gate: enable-assets - responses: - "200": - description: Scan cancelled - content: - application/json: - schema: - type: object - properties: - status: - type: string - - /api/assets/prune: - post: - operationId: pruneAssets - tags: [assets] - summary: Mark assets whose backing files no longer exist on disk - description: Starts a background job that removes asset entries whose underlying content no longer exists on disk. - x-feature-gate: enable-assets - responses: - "200": - description: Prune result - content: - application/json: - schema: - type: object - properties: - status: - type: string - marked: - type: integer - description: Number of assets marked as missing - - # =========================================================================== - # Cloud-runtime FE-facing operations - # - # These operations are served by the cloud runtime. The local runtime returns - # 404 for all of these paths. Each operation is tagged x-runtime: [cloud]. - # =========================================================================== - - # --------------------------------------------------------------------------- - # Jobs / prompts (cloud) - # --------------------------------------------------------------------------- - /api/jobs/{job_id}/cancel: - post: - operationId: cancelJob - tags: [queue] - summary: Cancel a running or pending job - description: "[cloud-only] Requests cancellation of a job. If the job is currently executing, execution is interrupted. If it is pending in the queue, it is removed." - x-runtime: [cloud] - parameters: - - name: job_id - in: path - required: true - schema: - type: string - format: uuid - description: The job ID to cancel. - responses: - "200": - description: Cancellation accepted - content: - application/json: - schema: - $ref: "#/components/schemas/JobCancelResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: Bad Request - job_id is not a valid UUID (emitted by request validation before the handler runs) - content: - application/json: - schema: - $ref: '#/components/schemas/BindingErrorResponse' - '500': - description: Internal server error - cancellation failed - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/job/{job_id}/status: - get: - operationId: getJobStatus - tags: [queue] - summary: Get status of a cloud job - deprecated: true - description: | - **Deprecated.** This endpoint is superseded by `GET /api/jobs/{job_id}`. - Clients should migrate; the endpoint is retained for backward - compatibility but will be removed in a future release. - x-runtime: [cloud] - parameters: - - name: job_id - in: path - required: true - schema: - type: string - format: uuid - description: The job ID to check status for. - responses: - "200": - description: Job status - content: - application/json: - schema: - $ref: "#/components/schemas/JobStatusResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '403': - description: Forbidden - job belongs to another user - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/prompt/{prompt_id}: - get: - operationId: getCloudPrompt - tags: [prompt] - summary: Get a cloud prompt by ID - description: "[cloud-only] Returns the full prompt record for a cloud-executed prompt, including the submitted workflow graph and execution metadata." - x-runtime: [cloud] - parameters: - - name: prompt_id - in: path - required: true - schema: - type: string - format: uuid - description: The prompt ID to fetch. - responses: - "200": - description: Cloud prompt detail - content: - application/json: - schema: - $ref: "#/components/schemas/CloudPrompt" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/history_v2: - get: - operationId: getHistory - tags: [history] - summary: Get paginated execution history (v2) - deprecated: true - description: | - **Deprecated.** This endpoint is superseded by `GET /api/jobs`. - Clients should migrate; the endpoint is retained for backward - compatibility but will be removed in a future release. - x-runtime: [cloud] - parameters: - - name: limit - in: query - schema: - type: integer - default: 20 - description: Maximum number of results - - name: offset - in: query - schema: - type: integer - default: 0 - description: Pagination offset - - name: status - in: query - schema: - type: string - description: Filter by execution status - responses: - "200": - description: History list - content: - application/json: - schema: - $ref: "#/components/schemas/HistoryResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/history_v2/{prompt_id}: - get: - operationId: getHistoryForPrompt - tags: [history] - summary: Get v2 history for a specific prompt - deprecated: true - description: | - **Deprecated.** This endpoint is superseded by `GET /api/jobs/{prompt_id}`. - Clients should migrate; the endpoint is retained for backward - compatibility but will be removed in a future release. - x-runtime: [cloud] - parameters: - - name: prompt_id - in: path - required: true - schema: - type: string - format: uuid - description: The prompt ID to fetch history for. - responses: - "200": - description: History entry - content: - application/json: - schema: - $ref: "#/components/schemas/HistoryDetailResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/logs: - get: - operationId: getLogs - tags: [system] - summary: Get cloud execution logs - deprecated: true - description: | - **Deprecated.** This endpoint returns a static placeholder response and - provides no real log data. It is retained only to avoid breaking clients - that still call it. Clients should remove their dependency; the endpoint - will be removed in a future release. - x-runtime: [cloud] - parameters: - - name: job_id - in: query - schema: - type: string - description: Filter logs by job ID - - name: limit - in: query - schema: - type: integer - default: 100 - description: Maximum number of log entries - - name: offset - in: query - schema: - type: integer - default: 0 - description: Pagination offset - responses: - "200": - description: Log entries - content: - application/json: - schema: - $ref: "#/components/schemas/LogsResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - # --------------------------------------------------------------------------- - # Assets extensions (cloud) - # --------------------------------------------------------------------------- - /api/assets/download: - post: - operationId: createAssetDownload - tags: [assets] - summary: Download assets to cloud runtime - description: "[cloud-only] Initiates a download of one or more assets to the cloud runtime environment. Returns a task ID for tracking download progress via WebSocket." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - assets - properties: - assets: - type: array - items: - $ref: "#/components/schemas/AssetDownloadRequest" - description: Assets to download - responses: - "202": - description: Download task accepted - content: - application/json: - schema: - type: object - required: - - task_id - - status - properties: - task_id: - type: string - format: uuid - description: ID of the download task; use to poll status. - status: - type: string - enum: [created, running, completed, failed] - description: Current task status (typically `created` on initial creation). - message: - type: string - description: Human-readable task message. - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '200': - description: File already exists in storage - asset created/returned immediately - content: - application/json: - schema: - $ref: '#/components/schemas/AssetCreated' - '422': - description: Validation errors - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/export: - post: - operationId: createAssetExport - tags: [assets] - summary: Export assets as a downloadable archive - description: "[cloud-only] Initiates a bulk export of assets. Returns a task ID for tracking progress via WebSocket. When complete, the export can be downloaded via the exports endpoint." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - job_ids: - type: array - items: - type: string - description: Job IDs whose associated assets should all be included in the ZIP bundle. - asset_ids: - type: array - items: - type: string - format: uuid - description: Asset IDs to include in the ZIP bundle. Additive to assets associated with provided job IDs. - export_name: - type: string - description: Name for the export archive - naming_strategy: - type: string - enum: [group_by_job_id, preserve, asset_id, group_by_job_time] - default: group_by_job_time - description: "Strategy for naming files in the ZIP: group by job ID, preserve original names, use the asset ID, or group by job creation time." - job_asset_name_filters: - type: object - additionalProperties: - type: array - minItems: 1 - items: - type: string - description: Optional per-job asset name filters. When provided for a job ID, only assets whose name matches one of the listed names are included. - responses: - "202": - description: Export task accepted - content: - application/json: - schema: - type: object - required: - - task_id - - status - properties: - task_id: - type: string - format: uuid - description: ID of the export task; use to poll status. - status: - type: string - enum: [created, running, completed, failed] - description: Current task status (typically `created` on initial creation). - message: - type: string - description: Human-readable task message. - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/exports/{exportName}: - get: - operationId: getAssetExport - tags: [assets] - summary: Download a completed asset export - description: "[cloud-only] Returns the archive file for a completed asset export." - x-runtime: [cloud] - parameters: - - name: exportName - in: path - required: true - schema: - type: string - description: Name of the export to download - responses: - "200": - description: Export archive file - content: - application/zip: - schema: - type: string - format: binary - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: Invalid export name - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/from-workflow: - post: - operationId: postAssetsFromWorkflow - tags: [assets] - summary: Create asset records from a workflow execution - description: "[cloud-only] Registers output files from a workflow execution as assets in the asset database." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - prompt_id - properties: - prompt_id: - type: string - format: uuid - description: Prompt ID whose outputs should be registered as assets - tags: - type: array - items: - type: string - description: Tags to apply to the created assets - responses: - "200": - description: Assets created or referenced - content: - application/json: - schema: - type: object - properties: - assets: - type: array - items: - $ref: "#/components/schemas/Asset" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/import: - post: - operationId: importPublishedAssets - tags: [assets] - summary: "[cloud-only] Import published assets into the caller's library" - description: | - [cloud-only] Imports the specified published assets into the caller's asset library. New DB records reference the same storage objects; no file copying occurs. Assets the caller already owns (by hash) are deduplicated. The `id` field on each returned `AssetInfo` is the caller's newly-created private asset ID, not the published asset ID supplied in the request. - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ImportPublishedAssetsRequest" - responses: - "200": - description: Successfully imported assets - content: - application/json: - schema: - $ref: "#/components/schemas/ImportPublishedAssetsResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/assets/remote-metadata: - get: - operationId: getRemoteAssetMetadata - tags: [assets] - summary: Fetch metadata for a remote asset URL - description: "[cloud-only] Fetches and returns metadata (content type, size, filename) for a remote URL without downloading the full content." - x-runtime: [cloud] - parameters: - - name: url - in: query - required: true - schema: - type: string - format: uri - description: URL to inspect - responses: - "200": - description: Remote metadata - content: - application/json: - schema: - $ref: "#/components/schemas/AssetMetadataResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '422': - description: Failed to retrieve metadata from source - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Custom nodes / hub (cloud) - # --------------------------------------------------------------------------- - /api/experiment/nodes: - get: - operationId: getNodeInfoSchema - tags: [runtime-only] - summary: Get pre-rendered node info schema - description: "[cloud-only] Returns the static ComfyUI object_info schema, identical for every caller, rendered once at startup with empty model/user-file context. Served by a raw HTTP handler that writes pre-rendered bytes with ETag + Cache-Control validators for RFC 7232 conditional GETs." - x-runtime: [cloud] - parameters: - - name: If-None-Match - in: header - required: false - schema: - type: string - description: Entity tag previously returned by this endpoint. When present and matching, the server returns 304 Not Modified. - responses: - "200": - description: Node info schema - headers: - ETag: - schema: - type: string - description: Entity tag for conditional request validation - Cache-Control: - schema: - type: string - description: Cache directives for the response - content: - application/json: - schema: - type: object - additionalProperties: - $ref: "#/components/schemas/NodeInfo" - "304": - description: Not Modified — returned when the client sends a matching If-None-Match header - post: - operationId: installCloudNode - tags: [node] - summary: Install a custom node package - description: "[cloud-only] Installs a custom node package in the cloud runtime by ID or repository URL." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: string - description: Node package ID or repository URL - version: - type: string - description: Specific version to install - responses: - "200": - description: Node installed - content: - application/json: - schema: - $ref: "#/components/schemas/CloudNode" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/experiment/nodes/{id}: - get: - operationId: getNodeByID - tags: [runtime-only] - summary: Get a single node definition by ID - description: "[cloud-only] Returns one node's definition from the pre-indexed object_info schema. Served by a raw HTTP handler that writes pre-rendered bytes with ETag + Cache-Control validators for RFC 7232 conditional GETs." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Node class identifier - - name: If-None-Match - in: header - required: false - schema: - type: string - description: Entity tag previously returned by this endpoint. When present and matching, the server returns 304 Not Modified. - responses: - "200": - description: Single node definition - headers: - ETag: - schema: - type: string - description: Entity tag for conditional request validation - Cache-Control: - schema: - type: string - description: Cache directives for the response - content: - application/json: - schema: - $ref: "#/components/schemas/NodeInfo" - "304": - description: Not Modified — returned when the client sends a matching If-None-Match header - "404": - description: Node not found - delete: - operationId: uninstallCloudNode - tags: [node] - summary: Uninstall a custom node package - description: "[cloud-only] Removes a custom node package from the cloud runtime." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: Custom node package ID - responses: - "204": - description: Node uninstalled - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/hub/assets/upload-url: - post: - operationId: createHubAssetUploadUrl - tags: [hub] - summary: Get a pre-signed upload URL for a hub asset - description: "[cloud-only] Returns a pre-signed URL that can be used to upload an asset file directly to storage." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - filename - - content_type - properties: - filename: - type: string - description: Name of the file to upload - content_type: - type: string - description: MIME type of the file - size: - type: integer - format: int64 - description: File size in bytes - responses: - "200": - description: Upload URL - content: - application/json: - schema: - type: object - properties: - upload_url: - type: string - format: uri - description: Pre-signed upload URL - asset_url: - type: string - format: uri - description: Public URL after upload completes - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/labels: - get: - operationId: listHubLabels - tags: [hub] - summary: List available hub labels - description: "[cloud-only] Returns the list of labels/categories available for tagging hub content." - x-runtime: [cloud] - responses: - "200": - description: Label list - content: - application/json: - schema: - $ref: "#/components/schemas/HubLabelListResponse" - '400': - description: Bad request (e.g. invalid type parameter) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/profiles: - get: - operationId: listHubProfiles - tags: [hub] - summary: List hub user profiles - description: "[cloud-only] Returns a paginated list of public hub user profiles." - x-runtime: [cloud] - parameters: - - name: limit - in: query - schema: - type: integer - description: Maximum number of results - - name: offset - in: query - schema: - type: integer - description: Pagination offset - - name: search - in: query - schema: - type: string - description: Search by username or display name - responses: - "200": - description: Profile list - content: - application/json: - schema: - type: object - properties: - profiles: - type: array - items: - $ref: "#/components/schemas/HubProfile" - total: - type: integer - has_more: - type: boolean - post: - operationId: createHubProfile - tags: [hub] - summary: Create a Hub profile - description: "[cloud-only] Creates a hub profile for the specified workspace. Username is immutable after creation." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateHubProfileRequest" - responses: - "201": - description: Hub profile created - content: - application/json: - schema: - $ref: "#/components/schemas/HubProfile" - "400": - description: Bad request (e.g. invalid username) - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "409": - description: Username already taken or profile already exists - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/profiles/{username}: - get: - operationId: getHubProfile - tags: [hub] - summary: Get a hub profile by username - description: "[cloud-only] Returns the public hub profile for the given username." - x-runtime: [cloud] - parameters: - - name: username - in: path - required: true - schema: - type: string - description: Hub username - responses: - "200": - description: Profile - content: - application/json: - schema: - $ref: "#/components/schemas/HubProfile" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/profiles/check: - get: - operationId: checkHubUsername - tags: [hub] - summary: Check if a hub username is available - description: "[cloud-only] Returns whether the given username is available for registration." - x-runtime: [cloud] - parameters: - - name: username - in: query - required: true - schema: - type: string - description: Username to check - responses: - "200": - description: Availability result - content: - application/json: - schema: - type: object - properties: - available: - type: boolean - username: - type: string - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/profiles/me: - get: - operationId: getMyHubProfile - tags: [hub] - summary: Get the authenticated user's hub profile - description: "[cloud-only] Returns the hub profile of the currently authenticated user." - x-runtime: [cloud] - responses: - "200": - description: Profile - content: - application/json: - schema: - $ref: "#/components/schemas/HubProfile" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '404': - description: No hub profile exists - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - put: - operationId: updateMyHubProfile - tags: [hub] - summary: Update the authenticated user's hub profile - description: "[cloud-only] Updates the hub profile of the currently authenticated user." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - username: - type: string - display_name: - type: string - bio: - type: string - avatar_url: - type: string - format: uri - links: - type: array - items: - type: string - format: uri - responses: - "200": - description: Updated profile - content: - application/json: - schema: - $ref: "#/components/schemas/HubProfile" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "409": - description: Conflict - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/hub/workflows: - get: - operationId: listHubWorkflows - tags: [hub] - summary: List published hub workflows - description: "[cloud-only] Returns a paginated list of publicly shared workflows on the hub." - x-runtime: [cloud] - parameters: - - name: limit - in: query - schema: - type: integer - description: Maximum number of results - - name: offset - in: query - schema: - type: integer - description: Pagination offset - - name: sort - in: query - schema: - type: string - description: Sort field (e.g. created_at, likes) - - name: order - in: query - schema: - type: string - enum: [asc, desc] - description: Sort direction - - name: search - in: query - schema: - type: string - description: Search by title or description - - name: labels - in: query - schema: - type: string - description: Filter by label IDs (comma-separated) - responses: - "200": - description: Hub workflow list - content: - application/json: - schema: - $ref: "#/components/schemas/HubWorkflowListResponse" - '400': - description: Bad request (e.g. malformed pagination cursor) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '404': - description: Profile not found (when filtering by username) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: publishHubWorkflow - tags: [hub] - summary: Publish a workflow to the hub - description: "[cloud-only] Publishes a workflow to the hub with metadata, thumbnail, and sample images." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PublishHubWorkflowRequest" - responses: - "200": - description: Workflow published to hub - content: - application/json: - schema: - $ref: "#/components/schemas/HubWorkflowDetail" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Workflow or profile not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/workflows/{share_id}: - get: - operationId: getHubWorkflow - tags: [hub] - summary: Get a published hub workflow by share ID - description: "[cloud-only] Returns the full details of a published workflow on the hub." - x-runtime: [cloud] - parameters: - - name: share_id - in: path - required: true - schema: - type: string - description: Workflow share ID - responses: - "200": - description: Hub workflow - content: - application/json: - schema: - $ref: "#/components/schemas/HubWorkflowDetail" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '413': - description: Workflow JSON too large - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: deleteHubWorkflow - tags: [hub] - summary: Unpublish a workflow from the hub - description: "[cloud-only] Removes a workflow from the hub listing." - x-runtime: [cloud] - parameters: - - name: share_id - in: path - required: true - schema: - type: string - description: Workflow share ID - responses: - "204": - description: Successfully unpublished - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Workflow not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/hub/workflows/index: - get: - operationId: listHubWorkflowIndex - tags: [hub] - summary: Get the hub workflow index - description: "[cloud-only] Returns the lightweight index of all hub workflows for client-side search and navigation." - x-runtime: [cloud] - responses: - "200": - description: Workflow index - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/HubWorkflowIndexEntry" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Workflows (cloud) - # --------------------------------------------------------------------------- - /api/workflows: - get: - operationId: listWorkflows - tags: [workflows] - summary: List cloud workflows - description: "[cloud-only] Returns a paginated list of the authenticated user's cloud workflows." - x-runtime: [cloud] - parameters: - - name: limit - in: query - schema: - type: integer - description: Maximum number of results - - name: offset - in: query - schema: - type: integer - description: Pagination offset - - name: sort - in: query - schema: - type: string - description: Sort field - - name: order - in: query - schema: - type: string - enum: [asc, desc] - description: Sort direction - - name: search - in: query - schema: - type: string - description: Search by workflow name - responses: - "200": - description: Workflow list - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowListResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: createWorkflow - tags: [workflows] - summary: Create a new cloud workflow - description: "[cloud-only] Creates a new cloud workflow with the provided name and optional initial content." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - properties: - name: - type: string - description: Workflow name - description: - type: string - description: Workflow description - content: - type: object - additionalProperties: true - description: Initial workflow graph JSON - responses: - "201": - description: Workflow created - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workflows/{workflow_id}: - get: - operationId: getWorkflow - tags: [workflows] - summary: Get a cloud workflow by ID - description: "[cloud-only] Returns the metadata for a cloud workflow." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - responses: - "200": - description: Workflow detail - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '403': - description: Forbidden - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - patch: - operationId: updateWorkflow - tags: [workflows] - summary: Update a cloud workflow - description: "[cloud-only] Updates the metadata (name, description) of an existing cloud workflow." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: - type: string - responses: - "200": - description: Workflow updated - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: deleteWorkflow - tags: [workflows] - summary: Delete a cloud workflow - description: "[cloud-only] Deletes a cloud workflow and all its versions." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - responses: - "204": - description: Workflow deleted - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workflows/{workflow_id}/content: - get: - operationId: getWorkflowContent - tags: [workflows] - summary: Get the content of a cloud workflow - description: "[cloud-only] Returns the full workflow graph JSON for the latest version of a cloud workflow." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - - name: version_id - in: query - schema: - type: string - description: Specific version ID to fetch - responses: - "200": - description: Workflow content - content: - application/json: - schema: - type: object - additionalProperties: true - description: The full workflow graph JSON - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '403': - description: Forbidden - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - put: - operationId: updateCloudWorkflowContent - tags: [workflows] - summary: Update the content of a cloud workflow - description: "[cloud-only] Saves new workflow graph JSON as a new version of the cloud workflow." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - requestBody: - required: true - content: - application/json: - schema: - type: object - additionalProperties: true - description: The workflow graph JSON to save - responses: - "200": - description: Content updated - content: - application/json: - schema: - $ref: "#/components/schemas/CloudWorkflowVersion" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/workflows/{workflow_id}/fork: - post: - operationId: forkWorkflow - tags: [workflows] - summary: Fork a cloud workflow - description: "[cloud-only] Creates a copy of a cloud workflow under the authenticated user's account." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID to fork. - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: Name for the forked workflow (defaults to original name) - responses: - "201": - description: Forked workflow - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '403': - description: Forbidden - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workflows/{workflow_id}/versions: - get: - operationId: listCloudWorkflowVersions - tags: [workflows] - summary: List versions of a cloud workflow - description: "[cloud-only] Returns the version history of a cloud workflow." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - - name: limit - in: query - schema: - type: integer - description: Maximum number of results - - name: offset - in: query - schema: - type: integer - description: Pagination offset - responses: - "200": - description: Version list - content: - application/json: - schema: - type: object - properties: - versions: - type: array - items: - $ref: "#/components/schemas/CloudWorkflowVersion" - total: - type: integer - has_more: - type: boolean - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - post: - operationId: createWorkflowVersion - tags: [workflows] - summary: Create a new cloud workflow version - description: "[cloud-only] Creates a new workflow version with updated workflow JSON. Uses optimistic concurrency via base_version." - x-runtime: [cloud] - parameters: - - name: workflow_id - in: path - required: true - schema: - type: string - format: uuid - description: The workflow ID. - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateWorkflowVersionRequest" - responses: - "201": - description: Version created - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowVersionResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden — not the workflow owner - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "409": - description: Version conflict — base_version does not match latest - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workflows/published/{share_id}: - get: - operationId: getPublishedWorkflow - tags: [workflows] - summary: Get a published workflow by share ID - description: "[cloud-only] Returns a publicly published cloud workflow by its share identifier." - x-runtime: [cloud] - parameters: - - name: share_id - in: path - required: true - schema: - type: string - description: The workflow share ID. - responses: - "200": - description: Published workflow - content: - application/json: - schema: - $ref: "#/components/schemas/PublishedWorkflowDetail" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '413': - description: Workflow JSON too large - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Auth / session (cloud) - # --------------------------------------------------------------------------- - /api/auth/session: - get: - operationId: getAuthSession - tags: [auth] - summary: Get the current authentication session - description: "[cloud-only] Returns the current session state for the authenticated user, including user identity and active workspace." - x-runtime: [cloud] - responses: - "200": - description: Session info - content: - application/json: - schema: - $ref: "#/components/schemas/AuthSession" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - post: - operationId: createSession - tags: [auth] - summary: Create a session cookie - description: "[cloud-only] Creates a session cookie from the bearer token in the Authorization header. Returns a Set-Cookie header with a secure HttpOnly session cookie. Cookie authentication is not allowed for this endpoint." - x-runtime: [cloud] - responses: - "200": - description: Session created - content: - application/json: - schema: - $ref: "#/components/schemas/CreateSessionResponse" - "400": - description: Bad request — invalid or expired ID token - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: deleteSession - tags: [auth] - summary: Delete session cookie (logout) - description: "[cloud-only] Clears the session cookie and optionally revokes the session on the server." - x-runtime: [cloud] - responses: - "200": - description: Session deleted - content: - application/json: - schema: - $ref: "#/components/schemas/DeleteSessionResponse" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/auth/token: - post: - operationId: exchangeToken - tags: [auth] - summary: Exchange credentials for an access token - description: "[cloud-only] Exchanges authentication credentials (e.g. an authorization code) for an access token." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - grant_type - properties: - grant_type: - type: string - enum: [authorization_code, refresh_token] - description: OAuth2 grant type - code: - type: string - description: Authorization code (for authorization_code grant) - refresh_token: - type: string - description: Refresh token (for refresh_token grant) - redirect_uri: - type: string - format: uri - description: Redirect URI used in the authorization request - responses: - "200": - description: Token response - content: - application/json: - schema: - $ref: "#/components/schemas/ExchangeTokenResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Workspace not found or user not a member - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /.well-known/jwks.json: - get: - operationId: getJwks - tags: [auth] - summary: Get JSON Web Key Set - description: "[cloud-only] Returns the JSON Web Key Set (JWKS) used to verify JWTs issued by the cloud authentication service." - x-runtime: [cloud] - responses: - "200": - description: JWKS - content: - application/json: - schema: - $ref: "#/components/schemas/JwksResponse" - - # --------------------------------------------------------------------------- - # OAuth 2.1 / RFC 7591 Dynamic Client Registration (cloud) - # --------------------------------------------------------------------------- - /.well-known/oauth-authorization-server: - get: - operationId: getOAuthAuthorizationServer - tags: [auth] - summary: "[cloud-only] OAuth 2.1 authorization-server metadata (RFC 8414)" - description: "[cloud-only] Public metadata document for OAuth 2.1 clients. Cached 5 minutes." - x-runtime: [cloud] - security: [] - responses: - "200": - description: Authorization-server metadata - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthAuthorizationServerMetadata" - "404": - description: OAuth disabled - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /.well-known/oauth-protected-resource: - get: - operationId: getOAuthProtectedResource - tags: [auth] - summary: "[cloud-only] OAuth 2.1 protected-resource metadata (RFC 9728)" - description: "[cloud-only] Public metadata describing the currently advertised protected resource. Cached 5 minutes." - x-runtime: [cloud] - security: [] - responses: - "200": - description: Protected-resource metadata - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthProtectedResourceMetadata" - "404": - description: OAuth disabled or no active resource configured - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /oauth/authorize: - get: - operationId: getOAuthAuthorize - tags: [auth] - summary: "[cloud-only] Begin or resume an OAuth 2.1 authorization request" - description: | - [cloud-only] Two modes: - - **Initial entry** (OAuth params present): validates client/redirect/resource/scopes, persists a server-side authorization-request row, and either redirects (no session / unverified email) to the configured frontend login URL carrying only the opaque `oauth_request_id`, or returns the JSON consent challenge for the frontend to render. - - **Resume** (`oauth_request_id` present): loads the server-side row, fails closed if expired/consumed/unknown, returns the JSON consent challenge. Browser-replayed OAuth params are intentionally ignored. - - The frontend renders the consent UI from the JSON payload and POSTs the user's decision back to this endpoint. - x-runtime: [cloud] - security: [] - parameters: - - { name: response_type, in: query, required: false, schema: { type: string } } - - { name: client_id, in: query, required: false, schema: { type: string } } - - { name: redirect_uri, in: query, required: false, schema: { type: string } } - - { name: scope, in: query, required: false, schema: { type: string } } - - name: state - in: query - required: false - schema: { type: string } - description: | - RFC 6749 §10.12 marks `state` as RECOMMENDED. Cloud hardening makes it REQUIRED on the initial-entry path (omitted only on the resume path where `oauth_request_id` is supplied instead). This parameter is `required: false` at the spec level only because the operation is dual-mode (initial entry vs. resume); the runtime rejects empty `state` on the initial-entry path with a stable `invalid_request` 400. - - { name: code_challenge, in: query, required: false, schema: { type: string } } - - { name: code_challenge_method, in: query, required: false, schema: { type: string } } - - { name: resource, in: query, required: false, schema: { type: string } } - - { name: oauth_request_id, in: query, required: false, schema: { type: string } } - responses: - "200": - description: Consent challenge payload (session present, email verified). Frontend renders the consent UI from this payload and POSTs back to /oauth/authorize. - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthConsentChallenge" - "302": - description: Redirect to login (no session / unverified email) or to registered redirect_uri (pre-validated client error) - headers: - Location: - schema: - type: string - "400": - description: Invalid authorize request (pre-redirect failure — unknown client, redirect mismatch, malformed params) - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: OAuth disabled - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - post: - operationId: postOAuthAuthorize - tags: [auth] - summary: "[cloud-only] Submit OAuth consent decision" - description: | - [cloud-only] JSON-only consent submission. The handler verifies the per-row CSRF token, atomically marks the authorization request consumed (single-use covers both allow and deny paths), then returns the redirect URL the browser must navigate to. The URL contains either `code` + original `state` for allow, or the RFC 6749 §5.2 error and `state` for deny. - - Workspace membership is re-checked at submission time. Consent is persisted keyed by `(user_id, client_id, resource_id, workspace_id)`; broadening the previously approved scope set requires a fresh consent flow. - x-runtime: [cloud] - security: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [oauth_request_id, csrf_token, decision, workspace_id] - properties: - oauth_request_id: { type: string, format: uuid } - csrf_token: { type: string } - decision: { type: string, enum: [allow, deny] } - workspace_id: { type: string } - responses: - "200": - description: Redirect URL for the frontend to navigate to (allow → with code+state; deny → with error+state) - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthAuthorizeRedirectResponse" - "400": - description: Bad request (CSRF mismatch, expired/consumed request, inaccessible workspace) - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Scope broadening on consent re-grant — fresh consent flow required - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: OAuth disabled - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /oauth/token: - post: - operationId: postOAuthToken - tags: [auth] - summary: "[cloud-only] Exchange authorization code or refresh token for a resource-bound access token" - description: | - [cloud-only] OAuth 2.1 token endpoint (RFC 6749 §3.2). Public clients only — `client_secret` is rejected. - - Two grant types are supported: - - `authorization_code` — exchanges the code minted by `/oauth/authorize` (with PKCE verifier) for an access token + first refresh token. Single-use; reuse fails closed. - - `refresh_token` — rotates the refresh token. Old token immediately invalid; presenting an already-rotated token revokes the entire token family and emits a security metric. - - Both grant types re-validate canonical user state, current workspace membership, and the resource's active flag at every mint. A code or refresh token bound to a deactivated resource fails closed. - - Errors follow RFC 6749 §5.2. Logs never contain raw codes, refresh tokens, or minted tokens. - - Per RFC 6749 §5.1, every 200 and 400 response carries `Cache-Control: no-store` and `Pragma: no-cache` so intermediaries cannot cache token-bearing or state-change-reason responses. - x-runtime: [cloud] - security: [] - requestBody: - required: true - content: - application/x-www-form-urlencoded: - schema: - type: object - required: [grant_type, client_id] - properties: - grant_type: { type: string, enum: [authorization_code, refresh_token] } - client_id: { type: string } - code: { type: string } - redirect_uri: { type: string } - code_verifier: { type: string } - refresh_token: { type: string } - scope: { type: string } - client_secret: { type: string } - responses: - "200": - description: New token pair - headers: - Cache-Control: - schema: - type: string - description: 'Always "no-store" per RFC 6749 §5.1' - Pragma: - schema: - type: string - description: 'Always "no-cache" per RFC 6749 §5.1' - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthTokenResponse" - "400": - description: RFC 6749 §5.2 error - headers: - Cache-Control: - schema: - type: string - description: 'Always "no-store" per RFC 6749 §5.1' - Pragma: - schema: - type: string - description: 'Always "no-cache" per RFC 6749 §5.1' - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthTokenError" - "404": - description: OAuth disabled - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /oauth/register: - post: - operationId: postOAuthRegister - tags: [auth] - summary: "[cloud-only] Dynamic Client Registration (RFC 7591)" - description: | - [cloud-only] Public, unauthenticated, insert-only RFC 7591 §3.1 client registration. Used by MCP-spec-compliant clients to self-register a public OAuth client without operator involvement. - - Policy: - - - Public clients only — `token_endpoint_auth_method` is forced to `none`. Confidential-client registration is out of scope this phase. - - Server-owned `resource_grants`. Caller-supplied `scope` or `resource_grants` is rejected as `invalid_client_metadata` (would be a privilege-escalation surface). Dynamic clients receive the same scopes the active resource publishes. - - Application-type-aware redirect URI policy. `application_type=native` accepts loopback (`127.0.0.1`, `::1`, `localhost`) and reverse-DNS-shaped custom schemes; `application_type=web` accepts HTTPS to hosts in an operator-controlled allowlist only. `application_type` is REQUIRED on the request — missing or empty rejects with `invalid_client_metadata`. - - Anti-impersonation: reserved client names are rejected from third parties via NFKC-folded compare. - - Generated `client_id` carries a stable prefix to distinguish dynamic from seeded clients in audit logs. - - Cache-Control: `no-store` on every 201 and 400 response (the response carries fresh credentials and rejection reasons). - x-runtime: [cloud] - security: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthRegisterRequest" - responses: - "201": - description: Registered. Body echoes the metadata RFC 7591 §3.2.1 requires. - headers: - Cache-Control: - schema: - type: string - description: 'Always "no-store"' - Pragma: - schema: - type: string - description: 'Always "no-cache"' - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthRegisterResponse" - "400": - description: RFC 7591 §3.2.2 invalid client metadata - headers: - Cache-Control: - schema: - type: string - description: 'Always "no-store"' - Pragma: - schema: - type: string - description: 'Always "no-cache"' - content: - application/json: - schema: - $ref: "#/components/schemas/OAuthRegisterError" - "404": - description: OAuth disabled - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "503": - description: No active resource is configured — DCR cannot mint a usable client until an active resource row is seeded. - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - # --------------------------------------------------------------------------- - # Billing (cloud) - # --------------------------------------------------------------------------- - /api/billing/balance: - get: - operationId: getBillingBalance - tags: [billing] - summary: Get current credit balance - description: "[cloud-only] Returns the authenticated user's current credit balance and usage summary." - x-runtime: [cloud] - responses: - "200": - description: Balance info - content: - application/json: - schema: - $ref: "#/components/schemas/BillingBalanceResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/events: - get: - operationId: getBillingEvents - tags: [billing] - summary: List billing events - description: "[cloud-only] Returns a paginated list of billing events (charges, credits, refunds) for the authenticated user." - x-runtime: [cloud] - parameters: - - name: limit - in: query - schema: - type: integer - description: Maximum number of results - - name: offset - in: query - schema: - type: integer - description: Pagination offset - - name: type - in: query - schema: - type: string - description: Filter by event type - responses: - "200": - description: Billing events - content: - application/json: - schema: - $ref: "#/components/schemas/BillingEventsResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/ops/{id}: - get: - operationId: getBillingOpStatus - tags: [billing] - summary: Get a billing operation by ID - description: "[cloud-only] Returns details of a specific billing operation." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: The billing operation ID. - responses: - "200": - description: Billing operation - content: - application/json: - schema: - $ref: "#/components/schemas/BillingOpStatusResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/payment-portal: - post: - operationId: getPaymentPortal - tags: [billing] - summary: Create a payment portal session - description: "[cloud-only] Creates a Stripe customer portal session for managing payment methods and invoices. Returns a URL to redirect the user to." - x-runtime: [cloud] - responses: - "200": - description: Portal session - content: - application/json: - schema: - type: object - properties: - url: - type: string - format: uri - description: Stripe portal URL - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: Bad request (e.g., missing return_url) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/plans: - get: - operationId: getBillingPlans - tags: [billing] - summary: List available billing plans - description: "[cloud-only] Returns the list of available subscription plans and their pricing." - x-runtime: [cloud] - responses: - "200": - description: Plan list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/BillingPlan" - - '401': - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/preview-subscribe: - post: - operationId: previewSubscribe - tags: [billing] - summary: Preview a subscription change - description: "[cloud-only] Returns a preview of what a subscription change would cost, including prorations." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - plan_id - properties: - plan_id: - type: string - description: ID of the plan to preview - responses: - "200": - description: Subscription preview - content: - application/json: - schema: - $ref: "#/components/schemas/PreviewSubscribeResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/status: - get: - operationId: getBillingStatus - tags: [billing] - summary: Get billing status - description: "[cloud-only] Returns the authenticated user's current billing and subscription status." - x-runtime: [cloud] - responses: - "200": - description: Billing status - content: - application/json: - schema: - $ref: "#/components/schemas/BillingStatusResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Workspace not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/subscribe: - post: - operationId: subscribe - tags: [billing] - summary: Subscribe to a billing plan - description: "[cloud-only] Creates a new subscription to the specified billing plan." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - plan_id - properties: - plan_id: - type: string - description: ID of the plan to subscribe to - payment_method_id: - type: string - description: Stripe payment method ID - responses: - "200": - description: Subscription created - content: - application/json: - schema: - $ref: "#/components/schemas/SubscribeResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/subscription/cancel: - post: - operationId: cancelSubscription - tags: [billing] - summary: Cancel the active subscription - description: "[cloud-only] Cancels the authenticated user's active subscription. The subscription remains active until the end of the current billing period." - x-runtime: [cloud] - responses: - "200": - description: Subscription cancelled - content: - application/json: - schema: - $ref: "#/components/schemas/CancelSubscriptionResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: Invalid request (e.g., no active subscription) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/subscription/resubscribe: - post: - operationId: resubscribe - tags: [billing] - summary: Resubscribe after cancellation - description: "[cloud-only] Reactivates a subscription that was previously cancelled but has not yet expired." - x-runtime: [cloud] - responses: - "200": - description: Subscription reactivated - content: - application/json: - schema: - $ref: "#/components/schemas/ResubscribeResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: Invalid request (e.g., no active subscription, not in cancellation grace period) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/billing/topup: - post: - operationId: createTopup - tags: [billing] - summary: Purchase additional credits - description: "[cloud-only] Purchases a one-time credit top-up using the user's payment method on file." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - amount - properties: - amount: - type: integer - description: Number of credits to purchase - responses: - "200": - description: Top-up successful - content: - application/json: - schema: - $ref: "#/components/schemas/CreateTopupResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # Workspace (cloud) - # --------------------------------------------------------------------------- - /api/workspace/api-keys: - get: - operationId: listWorkspaceAPIKeys - tags: [workspace] - summary: List workspace API keys - description: "[cloud-only] Returns the list of API keys for the current workspace." - x-runtime: [cloud] - responses: - "200": - description: API key list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkspaceApiKey" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: createWorkspaceAPIKey - tags: [workspace] - summary: Create a workspace API key - description: "[cloud-only] Creates a new API key for the current workspace." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - properties: - name: - type: string - description: Display name for the API key - description: - type: string - description: User-provided description of the key's purpose - maxLength: 5000 - responses: - "201": - description: API key created - content: - application/json: - schema: - $ref: "#/components/schemas/CreateWorkspaceAPIKeyResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Workspace not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '429': - description: Key limit reached - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/api-keys/{id}: - delete: - operationId: revokeWorkspaceAPIKey - tags: [workspace] - summary: Delete a workspace API key - description: "[cloud-only] Revokes and deletes a workspace API key." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: The API key ID. - responses: - "204": - description: API key deleted - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/invites: - get: - operationId: listWorkspaceInvites - tags: [workspace] - summary: List pending workspace invites - description: "[cloud-only] Returns the list of pending invitations for the current workspace." - x-runtime: [cloud] - responses: - "200": - description: Invite list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkspaceInvite" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: createWorkspaceInvite - tags: [workspace] - summary: Invite a user to the workspace - description: "[cloud-only] Creates an invitation for a user to join the current workspace." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - email - properties: - email: - type: string - format: email - description: Email address to invite - role: - type: string - enum: [admin, member] - description: Role to assign - responses: - "201": - description: Invite created - content: - application/json: - schema: - $ref: "#/components/schemas/PendingInvite" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "409": - description: Conflict - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Workspace not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/invites/{inviteId}: - delete: - operationId: revokeWorkspaceInvite - tags: [workspace] - summary: Cancel a workspace invite - description: "[cloud-only] Cancels a pending workspace invitation." - x-runtime: [cloud] - parameters: - - name: inviteId - in: path - required: true - schema: - type: string - description: The invite ID. - responses: - "204": - description: Invite cancelled - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/leave: - post: - operationId: leaveWorkspace - tags: [workspace] - summary: Leave the current workspace - description: "[cloud-only] Removes the authenticated user from the current workspace." - x-runtime: [cloud] - responses: - "204": - description: Left workspace - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Workspace not found or not a member - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/members: - get: - operationId: listWorkspaceMembers - tags: [workspace] - summary: List workspace members - description: "[cloud-only] Returns the list of members in the current workspace." - x-runtime: [cloud] - responses: - "200": - description: Member list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkspaceMember" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Workspace not found - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/members/{user_id}/api-keys: - get: - operationId: listMemberApiKeys - tags: [workspace] - summary: List API keys for a workspace member - description: "[cloud-only] Returns the API keys belonging to a specific workspace member. Requires admin role." - x-runtime: [cloud] - parameters: - - name: user_id - in: path - required: true - schema: - type: string - description: The member's user ID. - responses: - "200": - description: API key list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/WorkspaceApiKey" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - delete: - operationId: bulkRevokeWorkspaceMemberAPIKeys - tags: [workspace] - summary: Bulk revoke a member's API keys - description: "[cloud-only] Revokes all active API keys for a specific workspace member. Only workspace owners can perform this action." - x-runtime: [cloud] - parameters: - - name: user_id - in: path - required: true - schema: - type: string - minLength: 1 - description: The member's user ID. - responses: - "200": - description: Keys revoked - content: - application/json: - schema: - $ref: "#/components/schemas/BulkRevokeAPIKeysResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden — must be workspace owner - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '422': - description: Validation error (e.g. empty user_id) - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspace/members/{userId}: - patch: - operationId: updateWorkspaceMember - tags: [workspace] - summary: Update a workspace member's role - description: "[cloud-only] Updates the role of a workspace member. Requires admin role." - x-runtime: [cloud] - parameters: - - name: userId - in: path - required: true - schema: - type: string - description: The member's user ID. - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - role - properties: - role: - type: string - enum: [admin, member] - description: New role to assign - responses: - "200": - description: Member updated - content: - application/json: - schema: - $ref: "#/components/schemas/WorkspaceMember" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - delete: - operationId: removeWorkspaceMember - tags: [workspace] - summary: Remove a member from the workspace - description: "[cloud-only] Removes a member from the current workspace. Requires admin role." - x-runtime: [cloud] - parameters: - - name: userId - in: path - required: true - schema: - type: string - description: The member's user ID. - responses: - "204": - description: Member removed - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspaces: - get: - operationId: listWorkspaces - tags: [workspace] - summary: List workspaces the user belongs to - description: "[cloud-only] Returns the list of workspaces the authenticated user is a member of." - x-runtime: [cloud] - responses: - "200": - description: Workspace list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Workspace" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '404': - description: Feature not enabled for user - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: createWorkspace - tags: [workspace] - summary: Create a new workspace - description: "[cloud-only] Creates a new workspace. The authenticated user becomes the owner." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - properties: - name: - type: string - description: Workspace name - responses: - "201": - description: Workspace created - content: - application/json: - schema: - $ref: "#/components/schemas/Workspace" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '404': - description: Feature not enabled for user - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/workspaces/{id}: - get: - operationId: getWorkspace - tags: [workspace] - summary: Get a workspace by ID - description: "[cloud-only] Returns details of a workspace the user is a member of." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: The workspace ID. - responses: - "200": - description: Workspace detail - content: - application/json: - schema: - $ref: "#/components/schemas/Workspace" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - patch: - operationId: updateWorkspace - tags: [workspace] - summary: Update workspace settings - description: "[cloud-only] Updates the name or settings of a workspace. Requires admin role." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: The workspace ID. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: New workspace name - responses: - "200": - description: Workspace updated - content: - application/json: - schema: - $ref: "#/components/schemas/Workspace" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: deleteWorkspace - tags: [workspace] - summary: Delete a workspace - description: "[cloud-only] Soft-deletes a workspace. Requires owner role. Personal workspaces cannot be deleted." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: The workspace ID. - responses: - "204": - description: Workspace deleted - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "403": - description: Forbidden — must be workspace owner - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - # --------------------------------------------------------------------------- - # User / settings / misc (cloud) - # --------------------------------------------------------------------------- - /api/feedback: - post: - operationId: submitFeedback - tags: [user] - summary: Submit user feedback - description: "[cloud-only] Submits feedback from the user about their experience with the cloud runtime." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/FeedbackRequest" - responses: - "201": - description: Feedback submitted - content: - application/json: - schema: - type: object - properties: - id: - type: string - status: - type: string - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/files/mask-layers: - get: - operationId: getMaskLayers - tags: [assets] - summary: Get related mask layer filenames - description: "[cloud-only] Given a mask file (any of the 4 layers), returns all related mask layer filenames. Used by the mask editor to load the paint, mask, and painted layers when reopening a previously edited mask." - x-runtime: [cloud] - parameters: - - name: filename - in: query - required: true - schema: - type: string - description: Hash filename of any mask layer file - responses: - "200": - description: Related mask layers - content: - application/json: - schema: - type: object - properties: - mask: - type: string - description: Filename of the mask layer - nullable: true - paint: - type: string - description: Filename of the paint strokes layer - nullable: true - painted: - type: string - description: Filename of the painted image layer - nullable: true - painted_masked: - type: string - description: Filename of the final composite layer - nullable: true - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: File not found or not a mask file - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/internal/cloud_analytics: - post: - operationId: postCloudAnalytics - tags: [internal] - summary: Post client analytics events - description: "[cloud-only] Receives analytics events from the frontend for processing by the cloud analytics pipeline." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - events - properties: - events: - type: array - items: - type: object - required: - - event_name - properties: - event_name: - type: string - timestamp: - type: string - format: date-time - properties: - type: object - additionalProperties: true - responses: - "200": - description: Events accepted - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/invites/{token}/accept: - post: - operationId: acceptWorkspaceInvite - tags: [workspace] - summary: Accept a workspace invitation - description: "[cloud-only] Accepts a workspace invitation using the invite token. The authenticated user is added to the workspace." - x-runtime: [cloud] - parameters: - - name: token - in: path - required: true - schema: - type: string - description: The invitation token. - responses: - "200": - description: Invite accepted - content: - application/json: - schema: - $ref: "#/components/schemas/AcceptInviteResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '403': - description: Email does not match invite - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '409': - description: Already a member of this workspace - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/secrets: - get: - operationId: listSecrets - tags: [settings] - summary: List user secrets - description: "[cloud-only] Returns the list of secrets (API keys for third-party services) stored for the authenticated user. Secret values are redacted." - x-runtime: [cloud] - responses: - "200": - description: Secret list - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/SecretMeta" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '503': - description: Service unavailable - feature is disabled - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: createSecret - tags: [settings] - summary: Create or update a secret - description: "[cloud-only] Stores a new secret or updates an existing one. Secrets are encrypted at rest." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - name - - value - properties: - name: - type: string - description: Secret name (unique per user) - value: - type: string - description: Secret value - responses: - "201": - description: Secret created - content: - application/json: - schema: - $ref: "#/components/schemas/SecretResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '409': - description: Conflict - secret with this name or provider already exists - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '503': - description: Service unavailable - secrets feature disabled - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/secrets/{id}: - get: - operationId: getSecret - tags: [settings] - summary: Get secret metadata - description: "[cloud-only] Returns metadata for a specific secret. Does not return the plaintext secret value." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - format: uuid - description: The secret ID. - responses: - "200": - description: Secret metadata - content: - application/json: - schema: - $ref: "#/components/schemas/SecretResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '403': - description: Forbidden - user does not own this secret - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '503': - description: Service unavailable - secrets feature disabled - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - patch: - operationId: updateSecret - tags: [settings] - summary: Update a secret - description: "[cloud-only] Updates an existing secret's name and/or value. Both fields are optional; only provided fields are updated." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - format: uuid - description: The secret ID. - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateSecretRequest" - responses: - "200": - description: Secret updated - content: - application/json: - schema: - $ref: "#/components/schemas/SecretResponse" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "409": - description: Conflict — a secret with this name already exists - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '403': - description: Forbidden - user does not own this secret - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '503': - description: Service unavailable - secrets feature disabled - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - delete: - operationId: deleteSecret - tags: [settings] - summary: Delete a secret - description: "[cloud-only] Permanently deletes a stored secret." - x-runtime: [cloud] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: The secret ID. - responses: - "204": - description: Secret deleted - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '403': - description: Forbidden - user does not own this secret - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '503': - description: Service unavailable - secrets feature disabled - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/user: - get: - operationId: getUser - tags: [user] - summary: Get the authenticated cloud user - description: "[cloud-only] Returns the profile and account information for the currently authenticated user." - x-runtime: [cloud] - responses: - "200": - description: User profile - content: - application/json: - schema: - $ref: "#/components/schemas/UserResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - put: - operationId: updateCloudUser - tags: [user] - summary: Update the authenticated cloud user profile - description: "[cloud-only] Updates the profile information for the currently authenticated user." - x-runtime: [cloud] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - display_name: - type: string - avatar_url: - type: string - format: uri - responses: - "200": - description: Updated profile - content: - application/json: - schema: - $ref: "#/components/schemas/CloudUser" - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/userdata/{file}/publish: - get: - operationId: getUserdataFilePublish - tags: [userdata] - summary: Get publish info for a userdata file - description: "[cloud-only] Returns the publish status and share info for a userdata workflow file." - x-runtime: [cloud] - parameters: - - name: file - in: path - required: true - schema: - type: string - description: File path relative to user data directory - responses: - "200": - description: Publish info (publish_time is null if never published) - content: - application/json: - schema: - $ref: "#/components/schemas/WorkflowPublishInfo" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Workflow not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - post: - operationId: postUserdataFilePublish - tags: [userdata] - summary: Publish a userdata file to the cloud - description: "[cloud-only] Makes a userdata file available via a public URL for sharing or embedding." - x-runtime: [cloud] - parameters: - - name: file - in: path - required: true - schema: - type: string - description: File path relative to user data directory - responses: - "200": - description: Published file URL - content: - application/json: - schema: - type: object - properties: - url: - type: string - format: uri - description: Public URL of the published file - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/vhs/queryvideo: - get: - operationId: getVhsQueryVideo - tags: [view] - summary: Query VHS video metadata - description: "[cloud-only] Returns metadata about a video file processed by the VHS (Video Helper Suite) integration." - x-runtime: [cloud] - parameters: - - name: filename - in: query - required: true - schema: - type: string - description: Video filename - - name: type - in: query - schema: - type: string - enum: [input, output, temp] - description: Directory type - - name: subfolder - in: query - schema: - type: string - description: Subfolder within the directory - responses: - "200": - description: Video metadata - content: - application/json: - schema: - type: object - additionalProperties: true - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '400': - description: 'Missing required query parameter. Produced by the oapi-codegen - wrapper via echo.NewHTTPError, so the body shape matches Echo''s - default HTTPError serialization rather than ErrorResponse. - ' - content: - application/json: - schema: - $ref: '#/components/schemas/BindingErrorResponse' - /api/vhs/viewaudio: - get: - operationId: viewVhsAudio - tags: [view] - summary: View or download VHS audio - description: "[cloud-only] Returns audio content from a VHS-processed file." - x-runtime: [cloud] - parameters: - - name: filename - in: query - required: true - schema: - type: string - description: Audio filename - - name: type - in: query - schema: - type: string - enum: [input, output, temp] - description: Directory type - - name: subfolder - in: query - schema: - type: string - description: Subfolder within the directory - responses: - "200": - description: Audio content - content: - audio/*: - schema: - type: string - format: binary - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/vhs/viewvideo: - get: - operationId: viewVhsVideo - tags: [view] - summary: View or download VHS video - description: "[cloud-only] Returns video content from a VHS-processed file." - x-runtime: [cloud] - parameters: - - name: filename - in: query - required: true - schema: - type: string - description: Video filename - - name: type - in: query - schema: - type: string - enum: [input, output, temp] - description: Directory type - - name: subfolder - in: query - schema: - type: string - description: Subfolder within the directory - responses: - "200": - description: Video content - content: - video/*: - schema: - type: string - format: binary - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/viewvideo: - get: - operationId: viewVideo - tags: [view] - summary: View or download a video file - deprecated: true - description: | - **Deprecated.** This endpoint is an alias of `GET /api/view` added for - legacy history-queue video playback. Callers should use `/api/view` - directly; the endpoint is retained for backward compatibility but will - be removed in a future release. - x-runtime: [cloud] - parameters: - - name: filename - in: query - required: true - schema: - type: string - description: Video filename - - name: type - in: query - schema: - type: string - enum: [input, output, temp] - description: Directory type - - name: subfolder - in: query - schema: - type: string - description: Subfolder within the directory - responses: - "200": - description: Video content - content: - video/*: - schema: - type: string - format: binary - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - /api/tasks: - get: - operationId: listTasks - tags: [task] - summary: List background tasks - description: "[cloud-only] Retrieve a paginated list of background tasks for the authenticated user. Supports filtering by task type, status, and creation time." - x-runtime: [cloud] - parameters: - - name: task_name - in: query - schema: - type: string - description: Filter by task type name (exact match). - - name: idempotency_key - in: query - schema: - type: string - description: Filter by idempotency key (exact match). - - name: status - in: query - schema: - type: string - description: Filter by one or more statuses (comma-separated). - - name: created_after - in: query - schema: - type: string - format: date-time - description: Filter tasks created after this timestamp. - - name: created_before - in: query - schema: - type: string - format: date-time - description: Filter tasks created before this timestamp. - - name: sort_order - in: query - schema: - type: string - enum: [asc, desc] - default: desc - description: Sort direction by create_time. - - name: offset - in: query - schema: - type: integer - minimum: 0 - default: 0 - description: Pagination offset (0-based). - - name: limit - in: query - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - description: Maximum items per page (1-100). - responses: - "200": - description: Tasks retrieved - content: - application/json: - schema: - $ref: "#/components/schemas/TasksListResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "422": - description: Validation error - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /api/tasks/{task_id}: - get: - operationId: getTask - tags: [task] - summary: Get task details - description: "[cloud-only] Retrieve full details for a specific background task." - x-runtime: [cloud] - parameters: - - name: task_id - in: path - required: true - schema: - type: string - format: uuid - description: Task identifier (UUID). - responses: - "200": - description: Task details - content: - application/json: - schema: - $ref: "#/components/schemas/TaskResponse" - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - "404": - description: Task not found - content: - application/json: - schema: - $ref: "#/components/schemas/CloudError" - - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' components: - parameters: - ComfyUserHeader: - name: Comfy-User - in: header - required: false - schema: - type: string - description: | - Identifies the active user in multi-user mode. Used for settings, - userdata, and history isolation. This is not a security mechanism — - it is an organisational convenience with no authentication behind it. - - schemas: - # ------------------------------------------------------------------- - # Prompt - # ------------------------------------------------------------------- - PromptRequest: - type: object - description: A workflow submission. Wraps the prompt graph plus optional client identifier and extra per-request data. - required: - - prompt - properties: - prompt: - type: object - description: | - The workflow graph to execute. Keys are node IDs (strings); - values are objects with class_type and inputs. - additionalProperties: true - number: - type: number - description: Priority number for the queue (lower numbers have higher priority) - front: - type: boolean - description: If true, adds the prompt to the front of the queue - extra_data: - type: object - description: Extra data associated with the prompt (e.g. extra_pnginfo) - additionalProperties: true - client_id: - type: string - description: WebSocket client ID to receive progress updates - prompt_id: - type: string - format: uuid - description: "Client-supplied prompt ID. Server generates a UUID if omitted." - partial_execution_targets: - type: array - items: - type: string - description: List of node IDs to execute (partial graph execution) - workflow_id: - type: string - format: uuid - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Cloud workflow entity ID for tracking and gallery association. Ignored by local ComfyUI." - workflow_version_id: - type: string - format: uuid - nullable: true - x-runtime: [cloud] - description: "[cloud-only] Cloud workflow version ID for pinning execution to a specific version. Ignored by local ComfyUI." - - PromptResponse: - type: object - description: Server acknowledgement of a workflow submission. Includes the assigned `prompt_id` and current queue position. - properties: - prompt_id: - type: string - format: uuid - description: Unique identifier for the prompt execution - number: - type: number - description: Priority number in the queue - node_errors: - type: object - description: Validation errors keyed by node ID - additionalProperties: - $ref: "#/components/schemas/NodeError" - error: - description: Top-level prompt error (string message or structured error) - oneOf: - - type: string - - $ref: "#/components/schemas/PromptError" - - PromptErrorResponse: - type: object - description: Error response when prompt validation fails - additionalProperties: true - - PromptError: - type: object - description: Structured prompt validation error - properties: - type: - type: string - message: - type: string - details: - type: string - - Error: - type: object - description: Detailed node-level error - properties: - type: - type: string - message: - type: string - details: - type: string - extra_info: - type: object - properties: - input_name: - type: string - additionalProperties: true - - NodeError: - type: object - description: Error details for a single node - properties: - errors: - type: array - items: - $ref: "#/components/schemas/Error" - class_type: - type: string - description: The node's class type - dependent_outputs: - type: array - items: {} - - PromptInfo: - type: object - description: Summary of a queued or recently-executed prompt, as returned by the queue and history endpoints. - properties: - exec_info: - type: object - properties: - queue_remaining: - type: integer - description: Number of items remaining in the queue - - # ------------------------------------------------------------------- - # Queue - # ------------------------------------------------------------------- - QueueInfo: - type: object - description: Queue information with pending and running items - properties: - queue_running: - type: array - description: Currently running queue items - items: - type: array - description: | - Queue item tuple: [number, prompt_id, prompt, extra_data, outputs_to_execute, sensitive] - items: {} - prefixItems: - - type: number - description: Priority number - - type: string - format: uuid - description: prompt_id - - type: object - description: prompt graph - additionalProperties: true - - type: object - description: extra_data - additionalProperties: true - - type: array - description: outputs_to_execute (list of output node IDs) - items: - type: string - - type: object - description: sensitive data (may be omitted) - additionalProperties: true - queue_pending: - type: array - description: Pending queue items (oldest first) - items: - type: array - description: | - Queue item tuple: [number, prompt_id, prompt, extra_data, outputs_to_execute, sensitive] - items: {} - prefixItems: - - type: number - description: Priority number - - type: string - format: uuid - description: prompt_id - - type: object - description: prompt graph - additionalProperties: true - - type: object - description: extra_data - additionalProperties: true - - type: array - description: outputs_to_execute (list of output node IDs) - items: - type: string - - type: object - description: sensitive data (may be omitted) - additionalProperties: true - - QueueManageRequest: - type: object - description: Request to clear or delete from queue - properties: - clear: - type: boolean - description: If true, clear all pending items - delete: - type: array - items: - type: string - description: Array of prompt IDs to delete from queue - - QueueManageResponse: - type: object - x-runtime: [cloud] - description: >- - [cloud-only] Result of a queue mutation. The Cloud runtime returns which - items were deleted and whether the queue was cleared; local ComfyUI - returns an empty 200 body. - properties: - deleted: - type: array - nullable: true - items: - type: string - description: Prompt IDs that were deleted from the queue. - cleared: - type: boolean - nullable: true - description: Whether the queue was cleared. - - # ------------------------------------------------------------------- - # History - # ------------------------------------------------------------------- - HistoryEntry: - type: object - description: A single execution history entry - properties: - prompt: - type: array - description: | - Prompt tuple: [number, prompt_id, prompt_graph, extra_data, output_node_ids] - items: {} - outputs: - type: object - description: Output data from execution keyed by node ID - additionalProperties: true - status: - type: object - description: Execution status (status_str, completed, messages, etc.) - additionalProperties: true - meta: - type: object - description: Metadata about the execution and nodes - additionalProperties: true - - HistoryManageRequest: - type: object - description: Request to clear or delete history entries - properties: - clear: - type: boolean - description: If true, clear all history - delete: - type: array - items: - type: string - description: Array of prompt IDs to delete from history - - # ------------------------------------------------------------------- - # Jobs - # ------------------------------------------------------------------- - JobEntry: - type: object - description: Lightweight job data for list views - required: - - id - - status - properties: - id: - type: string - format: uuid - description: Unique job identifier (same as prompt_id) - status: - type: string - enum: - - pending - - in_progress - - completed - - failed - - cancelled - description: Current job status - create_time: - type: integer - format: int64 - description: Job creation timestamp (Unix milliseconds). - execution_start_time: - type: integer - format: int64 - description: Workflow execution start timestamp (Unix milliseconds, terminal states only). - execution_end_time: - type: integer - format: int64 - description: Workflow execution end timestamp (Unix milliseconds, terminal states only). - preview_output: - type: object - additionalProperties: true - description: Primary preview output - outputs_count: - type: integer - description: Total number of output files - workflow_id: - type: string - nullable: true - x-runtime: [cloud] - description: "[cloud-only] UUID of the Cloud workflow entity this job is associated with. Local ComfyUI returns null." - execution_error: - x-runtime: [cloud] - description: "[cloud-only] Detailed execution error from ComfyUI for failed jobs. Absent on local ComfyUI." - allOf: - - $ref: "#/components/schemas/ExecutionError" - - JobDetailResponse: - type: object - description: Full job details including workflow and outputs - required: - - id - - status - properties: - id: - type: string - format: uuid - status: - type: string - enum: - - pending - - in_progress - - completed - - failed - - cancelled - workflow: - type: object - additionalProperties: true - description: Full ComfyUI workflow - outputs: - type: object - additionalProperties: true - description: Full outputs object from execution - execution_error: - $ref: "#/components/schemas/ExecutionError" - create_time: - type: integer - format: int64 - description: Job creation timestamp (Unix milliseconds). - update_time: - type: integer - format: int64 - description: Last state-change timestamp (Unix milliseconds). - execution_start_time: - type: integer - format: int64 - description: Workflow execution start timestamp (Unix milliseconds, terminal states only). - execution_end_time: - type: integer - format: int64 - description: Workflow execution end timestamp (Unix milliseconds, terminal states only). - preview_output: - type: object - additionalProperties: true - outputs_count: - type: integer - execution_status: - type: object - additionalProperties: true - execution_meta: - type: object - additionalProperties: true - - ExecutionError: - type: object - description: Detailed execution error from ComfyUI - properties: - node_id: - type: string - description: ID of the node that failed - node_type: - type: string - description: Type name of the node - exception_message: - type: string - description: Human-readable error message - exception_type: - type: string - description: Python exception type - traceback: - type: array - items: - type: string - description: Traceback lines - current_inputs: - type: object - additionalProperties: true - current_outputs: - type: object - additionalProperties: true - - PaginationInfo: - type: object - description: Pagination metadata returned alongside list responses. - properties: - offset: - type: integer - limit: - type: integer - total: - type: integer - has_more: - type: boolean - - # ------------------------------------------------------------------- - # Upload / View - # ------------------------------------------------------------------- - UploadResult: - type: object - description: Response body returned by the image/mask upload endpoints, describing where the uploaded file now lives. - properties: - name: - type: string - description: Saved filename (may be renamed to avoid collisions) - subfolder: - type: string - description: Subfolder the file was saved to - type: - type: string - description: Directory type (input, temp) - - # ------------------------------------------------------------------- - # System - # ------------------------------------------------------------------- - DeviceStats: - type: object - description: GPU/compute device statistics - required: - - name - - type - - index - properties: - name: - type: string - description: Device name - type: - type: string - description: Device type (cuda, mps, cpu, etc.) - index: - type: number - nullable: true - description: | - Device index within its type (e.g. CUDA ordinal for `cuda:0`, - `cuda:1`). `null` for devices with no index, including the CPU - device returned in `--cpu` mode (PyTorch's `torch.device('cpu').index` - is `None`). - vram_total: - type: number - description: Total VRAM in bytes - vram_free: - type: number - description: Free VRAM in bytes - torch_vram_total: - type: number - description: Total PyTorch-managed VRAM in bytes - torch_vram_free: - type: number - description: Free PyTorch-managed VRAM in bytes - - SystemStatsResponse: - type: object - description: Hardware, VRAM, Python, and ComfyUI version information for the running process. - required: - - system - - devices - properties: - system: - type: object - required: - - os - - python_version - - embedded_python - - comfyui_version - - pytorch_version - - argv - - ram_total - - ram_free - properties: - os: - type: string - description: Operating system - python_version: - type: string - description: Python version - embedded_python: - type: boolean - description: Whether using embedded Python - comfyui_version: - type: string - description: ComfyUI version string - pytorch_version: - type: string - description: PyTorch version - required_frontend_version: - type: string - description: Required frontend version - argv: - type: array - items: - type: string - description: Command line arguments - ram_total: - type: number - description: Total RAM in bytes - ram_free: - type: number - description: Free RAM in bytes - installed_templates_version: - type: string - nullable: true - description: Version of the currently installed workflow templates - required_templates_version: - type: string - nullable: true - description: Minimum required workflow templates version for this ComfyUI build - comfy_package_versions: - type: array - description: Installed and required versions for every comfy* package pinned in requirements.txt - items: - type: object - required: - - name - - installed - - required - properties: - name: + schemas: + Asset: + description: Represents a user-owned asset (image, video, or other generated output). + properties: + asset_hash: + deprecated: true + description: 'Deprecated: use hash instead. Blake3 hash of the asset content.' + pattern: ^blake3:[a-f0-9]{64}$ type: string - installed: + created_at: + description: Timestamp when the asset was created + format: date-time type: string + display_name: + description: Display name of the asset. Mirrors name for backwards compatibility. nullable: true + type: string + hash: + description: Blake3 hash of the asset content. Preferred over asset_hash. + pattern: ^blake3:[a-f0-9]{64}$ + type: string + id: + description: Unique identifier for the asset + format: uuid + type: string + is_immutable: + description: Whether this asset is immutable (cannot be modified or deleted) + type: boolean + job_id: + description: ID of the job that created this asset, if available + format: uuid + nullable: true + type: string + last_access_time: + description: Timestamp when the asset was last accessed + format: date-time + type: string + metadata: + additionalProperties: true + description: System-managed metadata from download sources (HuggingFace, CivitAI, etc.) - read-only, not user-modifiable + readOnly: true + type: object + mime_type: + description: MIME type of the asset + type: string + name: + description: Name of the asset file + type: string + preview_id: + description: ID of the preview asset if available + format: uuid + nullable: true + type: string + preview_url: + description: URL for asset preview/thumbnail + format: uri + type: string + size: + description: Size of the asset in bytes + format: int64 + type: integer + tags: + description: Tags associated with the asset + items: + type: string + type: array + updated_at: + description: Timestamp when the asset was last updated + format: date-time + type: string + user_metadata: + additionalProperties: true + description: Custom user metadata for the asset + type: object + required: + - id + - name + - created_at + - updated_at + type: object + AssetCreated: + allOf: + - $ref: '#/components/schemas/Asset' + - properties: + created_new: + description: Whether this was a new asset creation (true) or returned existing (false) + type: boolean required: + - created_new + type: object + description: Response returned when a new asset is successfully created. + AssetInfo: + description: Lightweight asset reference used in workflow publishing payloads. + properties: + id: + description: Asset identifier. type: string + in_library: + description: Whether the caller already owns this asset. + type: boolean + model: + description: Whether this asset is a model. + type: boolean + name: + type: string + preview_url: + description: Signed URL for previewing the asset. + type: string + public: + description: Whether this is a public (platform-provided) asset. + type: boolean + storage_url: + type: string + required: + - id + - name + - preview_url + - storage_url + - model + - public + - in_library + type: object + AssetTagHistogramResponse: + description: Histogram of tag counts used for refining asset search results. + properties: + tag_counts: + additionalProperties: + type: integer + description: Map of tag names to their occurrence counts on matching assets + example: + checkpoint: 32 + lora: 193 + vae: 6 + type: object + required: + - tag_counts + type: object + AssetUpdated: + description: Response returned when an existing asset is successfully updated. + properties: + asset_hash: + deprecated: true + description: 'Deprecated: use hash instead. Blake3 hash of the asset content.' + pattern: ^blake3:[a-f0-9]{64}$ + type: string + display_name: + description: Display name of the asset. Mirrors name for backwards compatibility. nullable: true - devices: - type: array - items: - $ref: "#/components/schemas/DeviceStats" - - # ------------------------------------------------------------------- - # Node / Object Info - # ------------------------------------------------------------------- - NodeInfo: - type: object - description: 'Definition of a registered node class: its inputs, outputs, category, and display metadata.' - properties: - input: - type: object - description: Input specifications (required and optional groups) - additionalProperties: true - input_order: - type: object - description: Ordered input names per group - additionalProperties: - type: array - items: - type: string - output: - type: array - items: - type: string - description: Output type names - output_is_list: - type: array - items: - type: boolean - description: Whether each output is a list - output_name: - type: array - items: - type: string - description: Display names of outputs - name: - type: string - description: Internal class name - display_name: - type: string - description: Human-readable display name - description: - type: string - description: Node description - python_module: - type: string - description: Python module implementing the node - category: - type: string - description: Node category path - output_node: - type: boolean - description: Whether this is an output node - output_tooltips: - type: array - items: - type: string - description: Tooltips for each output - deprecated: - type: boolean - description: Whether the node is deprecated - experimental: - type: boolean - description: Whether the node is experimental - api_node: - type: boolean - description: Whether this is an API node - is_input_list: - type: boolean - description: Whether the node accepts list inputs - dev_only: - type: boolean - description: Whether the node is developer-only (hidden in production UI) - has_intermediate_output: - type: boolean - description: Whether the node emits intermediate output during execution - search_aliases: - type: array - items: - type: string - description: Alternative search terms for finding this node - essentials_category: - type: string - nullable: true - description: | - Category override used by the essentials pack. The - `essentials_category` key may be present with a string value, - present and `null`, or absent entirely: - - - V1 nodes: `essentials_category` is **omitted** when the node - class doesn't define an `ESSENTIALS_CATEGORY` attribute, and - **`null`** if the attribute is explicitly set to `None`. - - V3 nodes (`comfy_api.latest.io`): `essentials_category` is - **always present**, and **`null`** for nodes whose `Schema` - doesn't populate it. - - # ------------------------------------------------------------------- - # Models - # ------------------------------------------------------------------- - ModelFolder: - type: object - description: A configured model folder and the list of disk paths it resolves to. - required: - - name - - folders - properties: - name: - type: string - description: Model folder type name (e.g. "checkpoints") - folders: - type: array - items: - type: string - description: Filesystem paths for this model type - - ModelFile: - type: object - description: A single model file in a folder, with filesystem metadata. - required: - - name - - pathIndex - properties: - name: - type: string - description: Model filename - pathIndex: - type: integer - description: Index into the folder's paths array - modified: - type: number - description: File modification timestamp - created: - type: number - description: File creation timestamp - size: - type: integer - format: int64 - description: File size in bytes - - # ------------------------------------------------------------------- - # Subgraphs - # ------------------------------------------------------------------- - GlobalSubgraphInfo: - type: object - description: Metadata for a global subgraph blueprint (without full data) - required: - - source - - name - - info - properties: - source: - type: string - description: Source type ("templates" or "custom_node") - name: - type: string - description: Display name of the subgraph blueprint - info: - type: object - description: Additional information about the subgraph - required: - - node_pack - properties: - node_pack: - type: string - description: The node pack/module providing this subgraph - data: - type: string - description: The full subgraph JSON data (may be empty in list view) - - GlobalSubgraphData: - type: object - description: Full data for a global subgraph blueprint - required: - - source - - name - - info - - data - properties: - source: - type: string - description: Source type ("templates" or "custom_node") - name: - type: string - description: Display name of the subgraph blueprint - info: - type: object - description: Additional information about the subgraph - required: - - node_pack - properties: - node_pack: - type: string - description: The node pack/module providing this subgraph - data: - type: string - description: The full subgraph JSON data as a string - - # ------------------------------------------------------------------- - # Userdata - # ------------------------------------------------------------------- - UserDataResponse: - description: | - Response body for the POST endpoints `/api/userdata/{file}` and - `/api/userdata/{file}/move/{dest}`. Returns a single item whose - shape depends on the `full_info` query parameter. - x-variant-selector: - full_info=true: file-info object (`GetUserDataResponseFullFile`) - default: relative path string - oneOf: - - $ref: "#/components/schemas/GetUserDataResponseFullFile" - - type: string - description: Relative path of the written or moved file. Returned when `full_info` is absent or false. - - ListUserdataResponse: - description: | - Response body for `GET /api/userdata`. The array item shape is - determined by the `full_info` and `split` query parameters. - x-variant-selector: - full_info=true: array of file-info objects (`GetUserDataResponseFullFile`) - split=true: array of `[relative_path, ...path_components]` arrays - default: array of relative path strings - oneOf: - - type: array - items: - $ref: "#/components/schemas/GetUserDataResponseFullFile" - description: Returned when `full_info=true`. - - type: array - items: - type: array - items: - type: string - minItems: 2 - description: | - Returned when `split=true` and `full_info=false`. Each inner - array is `[relative_path, ...path_components]`. - - type: array - items: - type: string - description: Default shape — array of file paths relative to the user data root. - - GetUserDataResponseFullFile: - type: object - description: A single entry in a full-info user data listing. - properties: - path: - type: string - description: File name or path relative to the user directory - created: - type: number - description: Unix timestamp of file creation - size: - type: integer - description: File size in bytes - modified: - type: integer - format: int64 - description: Unix timestamp of last modification in milliseconds - - # ------------------------------------------------------------------- - # Assets - # ------------------------------------------------------------------- - Asset: - type: object - description: A registered asset — an input/output file tracked in the asset database with content hash and metadata. - required: - - id - - name - - size - - created_at - - updated_at - properties: - id: - type: string - format: uuid - description: Unique identifier for the asset - name: - type: string - description: Name of the asset file - hash: - type: string - nullable: true - description: Blake3 content hash of the asset (preferred over asset_hash) - pattern: "^blake3:[a-f0-9]{64}$" - asset_hash: - type: string - nullable: true - deprecated: true - description: "Deprecated: use `hash` instead. Blake3 hash of the asset content." - pattern: "^blake3:[a-f0-9]{64}$" - size: - type: integer - format: int64 - description: Size of the asset in bytes - mime_type: - type: string - description: MIME type of the asset - tags: - type: array - items: - type: string - description: Tags associated with the asset - user_metadata: - type: object - description: Custom user metadata - additionalProperties: true - metadata: - type: object - description: System-managed metadata (read-only) - additionalProperties: true - readOnly: true - preview_url: - type: string - format: uri - description: URL for asset preview/thumbnail - preview_id: - type: string - format: uuid - description: ID of the preview asset if available - prompt_id: - type: string - format: uuid - nullable: true - deprecated: true - description: "Deprecated: use job_id instead. ID of the prompt that created this asset." - job_id: - type: string - format: uuid - nullable: true - description: ID of the job that created this asset - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - last_access_time: - type: string - format: date-time - is_immutable: - type: boolean - description: Whether this asset is immutable - - AssetCreated: - description: Response body returned after successfully registering a new asset. - allOf: - - $ref: "#/components/schemas/Asset" - - type: object - required: - - created_new - properties: - created_new: - type: boolean - description: Whether this was a new creation (true) or returned existing (false) - - AssetUpdated: - type: object - description: Response body returned after updating an asset's metadata. - required: - - id - - updated_at - properties: - id: - type: string - format: uuid - name: - type: string - hash: - type: string - nullable: true - description: Blake3 content hash of the asset (preferred over asset_hash) - pattern: "^blake3:[a-f0-9]{64}$" - asset_hash: - type: string - nullable: true - deprecated: true - description: "Deprecated: use `hash` instead. Blake3 hash of the asset content." - pattern: "^blake3:[a-f0-9]{64}$" - tags: - type: array - items: - type: string - mime_type: - type: string - user_metadata: - type: object - additionalProperties: true - prompt_id: - type: string - format: uuid - nullable: true - deprecated: true - description: "Deprecated: use job_id instead. ID of the prompt that created this asset." - job_id: - type: string - format: uuid - nullable: true - description: ID of the job that created this asset - updated_at: - type: string - format: date-time - - ListAssetsResponse: - type: object - description: Paginated list of assets. - required: - - assets - - total - - has_more - properties: - assets: - type: array - items: - $ref: "#/components/schemas/Asset" - total: - type: integer - has_more: - type: boolean - - TagInfo: - type: object - description: A tag known to the asset database, with the number of assets bearing it. - required: - - name - - count - properties: - name: - type: string - count: - type: integer - - ListTagsResponse: - type: object - description: Flat list of all tags, with counts. - required: - - tags - - total - - has_more - properties: - tags: - type: array - items: - $ref: "#/components/schemas/TagInfo" - total: - type: integer - has_more: - type: boolean - - AssetTagHistogramResponse: - type: object - description: Tags that would refine a filtered asset query, with the count of assets each tag would additionally select. - required: - - tag_counts - properties: - tag_counts: - type: object - additionalProperties: - type: integer - description: Map of tag names to occurrence counts - - TagsModificationResponse: - type: object - description: Response body returned after adding or removing tags on an asset. - required: - - total_tags - properties: - added: - type: array - items: - type: string - description: Tags successfully added - removed: - type: array - items: - type: string - description: Tags successfully removed - already_present: - type: array - items: - type: string - description: Tags already present (for add) - not_present: - type: array - items: - type: string - description: Tags not present (for remove) - total_tags: - type: array - items: - type: string - description: All tags on the asset after the operation - - # ------------------------------------------------------------------- - # Result / Output types - # ------------------------------------------------------------------- - ResultItem: - type: object - description: A single output file reference - properties: - filename: - type: string - subfolder: - type: string - type: - type: string - enum: [input, output, temp] - display_name: - type: string - - NodeOutputs: - type: object - description: | - Outputs from a single node execution. Known keys are listed below, - but custom nodes may add arbitrary keys (additionalProperties). - properties: - images: - type: array - items: - $ref: "#/components/schemas/ResultItem" - audio: - type: array - items: - $ref: "#/components/schemas/ResultItem" - video: - type: array - items: - $ref: "#/components/schemas/ResultItem" - animated: - type: array - items: - type: boolean - text: - oneOf: - - type: string - - type: array - items: - type: string - additionalProperties: true - - TerminalSize: - type: object - description: Terminal dimensions - properties: - cols: - type: number - row: - type: number - - LogEntry: - type: object - description: A single log entry - properties: - t: - type: string - description: Timestamp - m: - type: string - description: Log message - - StatusWsMessageStatus: - type: object - description: Inner payload of a `status` WebSocket message, describing the execution queue state. - properties: - exec_info: - type: object - required: - - queue_remaining - properties: - queue_remaining: - type: integer - - StatusWsMessage: - type: object - description: Initial status message sent on connect + queue status updates - properties: - status: - $ref: "#/components/schemas/StatusWsMessageStatus" - sid: - type: string - description: Session ID assigned by the server - - ProgressWsMessage: - type: object - description: Node execution progress (step N of M) - required: - - value - - max - - prompt_id - - node - properties: - value: - type: integer - description: Current step - max: - type: integer - description: Total steps - prompt_id: - type: string - node: - type: string - description: Node ID currently executing - - ProgressTextWsMessage: - type: object - description: Text-based progress update from a node - properties: - nodeId: - type: string - text: - type: string - prompt_id: - type: string - - NodeProgressState: - type: object - description: Progress state for a single node - properties: - value: - type: number - max: - type: number - state: - type: string - enum: [pending, running, finished, error] - node_id: - type: string - prompt_id: - type: string - display_node_id: - type: string - parent_node_id: - type: string - real_node_id: - type: string - - ProgressStateWsMessage: - type: object - description: Bulk progress state for all nodes in a prompt - required: - - prompt_id - - nodes - properties: - prompt_id: - type: string - nodes: - type: object - description: Map of node ID to progress state - additionalProperties: - $ref: "#/components/schemas/NodeProgressState" - - ExecutingWsMessage: - type: object - description: Fired when a node begins execution - required: - - node - - display_node - - prompt_id - properties: - node: - type: string - description: Node ID - display_node: - type: string - description: Display node ID (may differ for subgraphs) - prompt_id: - type: string - - ExecutedWsMessage: - type: object - description: Fired when a node completes execution with output - required: - - node - - display_node - - prompt_id - - output - properties: - node: - type: string - display_node: - type: string - prompt_id: - type: string - output: - $ref: "#/components/schemas/NodeOutputs" - merge: - type: boolean - description: Whether to merge with existing output - - ExecutionWsMessageBase: - type: object - description: Base fields for execution lifecycle messages - required: - - prompt_id - - timestamp - properties: - prompt_id: - type: string - timestamp: - type: integer - description: Unix timestamp in milliseconds - - ExecutionStartWsMessage: - allOf: - - $ref: "#/components/schemas/ExecutionWsMessageBase" - description: Fired when prompt execution begins - - ExecutionSuccessWsMessage: - allOf: - - $ref: "#/components/schemas/ExecutionWsMessageBase" - description: Fired when prompt execution completes successfully - - ExecutionCachedWsMessage: - allOf: - - $ref: "#/components/schemas/ExecutionWsMessageBase" - - type: object - properties: - nodes: - type: array - items: - type: string - description: List of node IDs that were cached - description: Fired when nodes are served from cache - - ExecutionInterruptedWsMessage: - allOf: - - $ref: "#/components/schemas/ExecutionWsMessageBase" - - type: object - properties: - node_id: - type: string - node_type: - type: string - executed: - type: array - items: - type: string - description: Node IDs that completed before interruption - description: Fired when execution is interrupted by user - - ExecutionErrorWsMessage: - allOf: - - $ref: "#/components/schemas/ExecutionWsMessageBase" - - type: object - properties: - node_id: - type: string - node_type: - type: string - executed: - type: array - items: - type: string - exception_message: - type: string - exception_type: - type: string - traceback: - type: array - items: - type: string - current_inputs: {} - current_outputs: {} - description: Fired when a node throws an exception during execution - - LogsWsMessage: - type: object - description: Streaming log entries from the server - properties: - size: - $ref: "#/components/schemas/TerminalSize" - entries: - type: array - items: - $ref: "#/components/schemas/LogEntry" - - NotificationWsMessage: - type: object - description: Server notification (e.g. model download complete) - properties: - value: - type: string - id: - type: string - - FeatureFlagsWsMessage: - type: object - description: Feature flags sent on connect - additionalProperties: true - - AssetDownloadWsMessage: - type: object - description: Asset download progress - required: - - task_id - - asset_name - - bytes_total - - bytes_downloaded - - progress - - status - properties: - task_id: - type: string - asset_name: - type: string - bytes_total: - type: number - bytes_downloaded: - type: number - progress: - type: number - description: 0.0 to 1.0 - status: - type: string - enum: [created, running, completed, failed] - asset_id: - type: string - error: - type: string - - AssetExportWsMessage: - type: object - description: Bulk asset export progress - required: - - task_id - - assets_total - - assets_attempted - - assets_failed - - bytes_total - - bytes_processed - - progress - - status - properties: - task_id: - type: string - export_name: - type: string - assets_total: - type: number - assets_attempted: - type: number - assets_failed: - type: number - bytes_total: - type: number - bytes_processed: - type: number - progress: - type: number - description: 0.0 to 1.0 - status: - type: string - enum: [created, running, completed, failed] - error: - type: string - - # ------------------------------------------------------------------- - # Cloud-runtime schemas - # - # These schemas are exclusively referenced by cloud-runtime operations. - # Tagged x-runtime: [cloud]. - # ------------------------------------------------------------------- - CloudError: - type: object - x-runtime: [cloud] - description: "[cloud-only] Standard error response from cloud endpoints." - required: - - error - properties: - error: - type: string - description: Error message - code: - type: string - description: Machine-readable error code - details: - type: object - additionalProperties: true - description: Additional error context - - CloudJobStatus: - type: object - x-runtime: [cloud] - description: "[cloud-only] Status of a cloud job." - required: - - id - - status - properties: - id: - type: string - format: uuid - status: - type: string - enum: [pending, running, completed, failed, cancelled] - progress: - type: number - minimum: 0 - maximum: 1 - description: "Execution progress (0.0 to 1.0)" - started_at: - type: string - format: date-time - nullable: true - completed_at: - type: string - format: date-time - nullable: true - - CloudPrompt: - type: object - x-runtime: [cloud] - description: "[cloud-only] A cloud-executed prompt record." - required: - - id - - status - properties: - id: - type: string - format: uuid - status: - type: string - workflow: - type: object - additionalProperties: true - outputs: - type: object - additionalProperties: true - created_at: - type: string - format: date-time - completed_at: - type: string - format: date-time - nullable: true - - HistoryV2Response: - type: object - x-runtime: [cloud] - description: "[cloud-only] Paginated execution history in v2 format." - required: - - items - - total - - has_more - properties: - items: - type: array - items: - $ref: "#/components/schemas/HistoryV2Entry" - total: - type: integer - has_more: - type: boolean - - HistoryV2Entry: - type: object - x-runtime: [cloud] - description: "[cloud-only] A single execution history entry in v2 format." - required: - - id - - status - properties: - id: - type: string - format: uuid - status: - type: string - workflow: - type: object - additionalProperties: true - outputs: - type: object - additionalProperties: true - created_at: - type: string - format: date-time - started_at: - type: string - format: date-time - nullable: true - completed_at: - type: string - format: date-time - nullable: true - preview_output: - type: object - additionalProperties: true - - CloudLogsResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Paginated cloud execution logs." - required: - - entries - properties: - entries: - type: array - items: - type: object - properties: - timestamp: - type: string - format: date-time - level: - type: string - enum: [debug, info, warn, error] - message: - type: string - job_id: - type: string - format: uuid - total: - type: integer - has_more: - type: boolean - - AssetDownloadRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] A single asset to download to the cloud runtime." - required: - - asset_id - properties: - asset_id: - type: string - format: uuid - description: ID of the asset to download - target_path: - type: string - description: Target path on the runtime filesystem - - ImportPublishedAssetsRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] Request body for importing published assets into the caller's library." - required: - - published_asset_ids - properties: - published_asset_ids: - type: array - description: IDs of published assets (inputs and models) to import. - items: - type: string - share_id: - type: string - nullable: true - description: | - Optional. Share ID of the published workflow these assets belong to. When provided (non-null, non-empty): all `published_asset_ids` must belong to this share's workflow version; returns 400 if the share is not found or any asset does not belong to it. When omitted, null, or empty string: no share-scoped validation is performed and the assets are validated only against global rules (preserved for clients that have not yet adopted `share_id`). - - ImportPublishedAssetsResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Response after importing published assets. Each returned `AssetInfo.id` is the caller's newly-created private asset ID, not the published asset ID supplied in the request." - required: - - assets - properties: - assets: - type: array - items: - $ref: "#/components/schemas/AssetInfo" - - RemoteAssetMetadata: - type: object - x-runtime: [cloud] - description: "[cloud-only] Metadata fetched from a remote asset URL." - properties: - content_type: - type: string - description: MIME type of the remote file - content_length: - type: integer - format: int64 - description: Size in bytes - filename: - type: string - description: Suggested filename from Content-Disposition or URL - - CloudNode: - type: object - x-runtime: [cloud] - description: "[cloud-only] An installed custom node package in the cloud runtime." - required: - - id - - name - properties: - id: - type: string - name: - type: string - version: - type: string - description: - type: string - author: - type: string - repository: - type: string - format: uri - installed_at: - type: string - format: date-time - enabled: - type: boolean - - HubLabel: - type: object - x-runtime: [cloud] - description: "[cloud-only] A label/category used for tagging hub content." - required: - - id - - name - properties: - id: - type: string - name: - type: string - description: - type: string - color: - type: string - description: Hex color code for the label - - HubProfile: - type: object - x-runtime: [cloud] - description: "[cloud-only] A public user profile on the ComfyUI Hub." - required: - - username - properties: - username: - type: string - display_name: - type: string - bio: - type: string - avatar_url: - type: string - format: uri - links: - type: array - items: - type: string - format: uri - workflow_count: - type: integer - created_at: - type: string - format: date-time - - HubWorkflow: - type: object - x-runtime: [cloud] - description: "[cloud-only] A published workflow on the ComfyUI Hub." - required: - - share_id - - name - properties: - share_id: - type: string - name: - type: string - description: - type: string - author: - $ref: "#/components/schemas/HubProfile" - labels: - type: array - items: - $ref: "#/components/schemas/HubLabel" - thumbnail_url: - type: string - format: uri - content: - type: object - additionalProperties: true - description: Workflow graph JSON - likes: - type: integer - views: - type: integer - forks: - type: integer - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - HubWorkflowList: - type: object - x-runtime: [cloud] - description: "[cloud-only] Paginated list of hub workflows." - required: - - workflows - - total - - has_more - properties: - workflows: - type: array - items: - $ref: "#/components/schemas/HubWorkflow" - total: - type: integer - has_more: - type: boolean - - HubWorkflowIndexEntry: - type: object - x-runtime: [cloud] - description: "[cloud-only] Lightweight entry in the hub workflow index for client-side search." - required: - - share_id - - name - properties: - share_id: - type: string - name: - type: string - author_username: - type: string - labels: - type: array - items: - type: string - likes: - type: integer - updated_at: - type: string - format: date-time - - CloudWorkflow: - type: object - x-runtime: [cloud] - description: "[cloud-only] A cloud-managed workflow with version history." - required: - - id - - name - properties: - id: - type: string - format: uuid - name: - type: string - description: - type: string - share_id: - type: string - nullable: true - description: Public share identifier if published - latest_version_id: - type: string - format: uuid - nullable: true - thumbnail_url: - type: string - format: uri - nullable: true - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - CloudWorkflowList: - type: object - x-runtime: [cloud] - description: "[cloud-only] Paginated list of cloud workflows." - required: - - workflows - - total - - has_more - properties: - workflows: - type: array - items: - $ref: "#/components/schemas/CloudWorkflow" - total: - type: integer - has_more: - type: boolean - - CloudWorkflowVersion: - type: object - x-runtime: [cloud] - description: "[cloud-only] A version of a cloud workflow." - required: - - id - - workflow_id - properties: - id: - type: string - format: uuid - workflow_id: - type: string - format: uuid - version_number: - type: integer - created_at: - type: string - format: date-time - - AuthSession: - type: object - x-runtime: [cloud] - description: "[cloud-only] Current authentication session state." - required: - - user - properties: - user: - $ref: "#/components/schemas/CloudUser" - workspace: - $ref: "#/components/schemas/Workspace" - expires_at: - type: string - format: date-time - - AuthTokenResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] OAuth2 token response." - required: - - access_token - - token_type - properties: - access_token: - type: string - token_type: - type: string - description: Always "Bearer" - expires_in: - type: integer - description: Token lifetime in seconds - refresh_token: - type: string - nullable: true - scope: - type: string - - JwksResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] JSON Web Key Set for JWT verification." - required: - - keys - properties: - keys: - type: array - items: - type: object + type: string + hash: + description: Blake3 hash of the asset content. Preferred over asset_hash. + pattern: ^blake3:[a-f0-9]{64}$ + type: string + id: + description: Asset ID + format: uuid + type: string + job_id: + description: ID of the job that created this asset, if available + format: uuid + nullable: true + type: string + mime_type: + description: Updated MIME type of the asset + type: string + name: + description: Updated name of the asset + type: string + tags: + description: Tags associated with the asset + items: + type: string + type: array + updated_at: + description: Timestamp of the update + format: date-time + type: string + user_metadata: + additionalProperties: true + description: Updated custom metadata + type: object required: - - kty - - kid - - use + - id + - updated_at + type: object + CreateWorkflowRequest: + description: Request body for creating a new saved workflow. properties: - kty: - type: string - description: Key type (e.g. RSA) - kid: - type: string - description: Key ID - use: - type: string - description: Key use (e.g. sig) - alg: - type: string - description: Algorithm (e.g. RS256) - n: - type: string - description: RSA modulus (base64url) - e: - type: string - description: RSA exponent (base64url) - additionalProperties: true - - OAuthAuthorizationServerMetadata: - type: object - x-runtime: [cloud] - description: "[cloud-only] OAuth 2.1 authorization-server metadata (RFC 8414)." - required: - - issuer - - authorization_endpoint - - token_endpoint - - jwks_uri - - response_types_supported - - grant_types_supported - - code_challenge_methods_supported - - token_endpoint_auth_methods_supported - properties: - issuer: - type: string - format: uri - authorization_endpoint: - type: string - format: uri - token_endpoint: - type: string - format: uri - jwks_uri: - type: string - format: uri - registration_endpoint: - type: string - format: uri - description: "[cloud-only] RFC 7591 §3.1 Dynamic Client Registration endpoint. Advertised so MCP-spec-compliant clients can auto-discover and self-register without operator involvement. Present only when DCR is enabled." - response_types_supported: - type: array - items: - type: string - grant_types_supported: - type: array - items: - type: string - code_challenge_methods_supported: - type: array - items: - type: string - token_endpoint_auth_methods_supported: - type: array - items: - type: string - scopes_supported: - type: array - items: - type: string - - OAuthProtectedResourceMetadata: - type: object - x-runtime: [cloud] - description: "[cloud-only] OAuth 2.1 protected-resource metadata (RFC 9728)." - required: - - resource - - authorization_servers - - scopes_supported - properties: - resource: - type: string - format: uri - authorization_servers: - type: array - items: - type: string - format: uri - scopes_supported: - type: array - items: - type: string - bearer_methods_supported: - type: array - items: - type: string - - OAuthConsentChallenge: - type: object - x-runtime: [cloud] - description: "[cloud-only] Server-side state describing the OAuth consent decision the user is being asked to make. Returned by GET /oauth/authorize when a valid session exists; the frontend renders the consent UI from this payload and POSTs the decision back. Browser never sees the original OAuth params on resume." - required: - - oauth_request_id - - csrf_token - - client_display_name - - resource_display_name - - scopes - - workspaces - properties: - oauth_request_id: - type: string - format: uuid - description: Opaque server-side identifier for the authorization-request row. Carried back unchanged in the consent submission. - csrf_token: - type: string - description: Per-row CSRF token bound to this authorization request (not to the session). Must be echoed back on POST. - client_display_name: - type: string - description: Human-readable name of the OAuth client requesting authorization. - resource_display_name: - type: string - description: Human-readable name of the protected resource. - scopes: - type: array - description: Scopes the client is requesting for this resource. The frontend should present these for the user to approve. - items: - type: string - workspaces: - type: array - description: Workspaces the user can select from. Membership is re-checked on POST. - items: - $ref: "#/components/schemas/OAuthConsentChallengeWorkspace" - - OAuthConsentChallengeWorkspace: - type: object - x-runtime: [cloud] - description: "[cloud-only] One workspace option presented in the OAuth consent challenge." - required: [id, name, type, role] - properties: - id: { type: string } - name: { type: string } - type: { type: string, enum: [personal, team] } - role: { type: string, enum: [owner, member] } - - OAuthAuthorizeRedirectResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Redirect target produced after a JSON consent submission. The frontend must navigate the browser to this URL so custom-scheme client callbacks work without relying on fetch-visible 302 headers." - required: - - redirect_url - properties: - redirect_url: - type: string - format: uri - description: OAuth client redirect URI with either code+state for allow, or error+state for deny. - - OAuthTokenResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] RFC 6749 §5.1 successful token response." - required: [access_token, token_type, expires_in, refresh_token, scope] - properties: - access_token: - type: string - description: Resource-bound access token (audience matches the protected resource). - token_type: - type: string - enum: [Bearer] - expires_in: - type: integer - description: Access token lifetime in seconds. - refresh_token: - type: string - description: Opaque refresh token. Rotates on every successful refresh; presenting an already-rotated token revokes the entire family. - scope: - type: string - description: Space-delimited scopes granted with this token. - - OAuthTokenError: - type: object - x-runtime: [cloud] - description: "[cloud-only] RFC 6749 §5.2 error response." - required: [error] - properties: - error: - type: string - description: 'RFC 6749 §5.2 error code: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope.' - error_description: - type: string - description: Human-readable, no leak of internal storage state. - - OAuthRegisterRequest: - type: object - x-runtime: [cloud] - additionalProperties: false - description: "[cloud-only] RFC 7591 §2 client metadata document. Only the fields the server honors are listed; presence of `scope` or `resource_grants` in the request is rejected (`invalid_client_metadata`) because those are server-owned for dynamic clients." - required: - - redirect_uris - - application_type - properties: - redirect_uris: - type: array - items: - type: string - minItems: 1 - maxItems: 5 - description: 1–5 redirect URIs. Validated against `application_type` policy. - client_name: - type: string - maxLength: 100 - description: Human-readable name shown in the consent UI. Reserved-name list rejects impersonation of major clients. - application_type: - type: string - enum: [native, web] - description: | - RFC 7591 §2 application_type. **REQUIRED** — clients MUST declare intent; the server does not default this field. `native` for desktop / CLI / MCP-spec-strict clients (loopback redirects); `web` for hosted clients (HTTPS only, host must be allowlisted). A missing or explicitly empty `application_type` rejects with `invalid_client_metadata`. - token_endpoint_auth_method: - type: string - enum: [none] - description: 'Public clients only this phase — must be `none` if present. The server forces `none` regardless.' - grant_types: - type: array - items: - type: string - enum: [authorization_code, refresh_token] - description: Optional. Defaults to `["authorization_code","refresh_token"]`. - response_types: - type: array - items: - type: string - enum: [code] - description: Optional. Defaults to `["code"]`. - scope: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Dynamic clients do not pick scopes — the server assigns scopes from the active resource's published list. Sending `scope` in the registration body is treated as a privilege-escalation attempt and returns `invalid_client_metadata`." - resource_grants: - type: object - nullable: true - additionalProperties: - type: array - items: - type: string - description: "**REJECTED IF PRESENT.** Same reason as `scope`. The set of resources and scopes a dynamic client may request is server-policy, not request-driven." - client_uri: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - logo_uri: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - tos_uri: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - policy_uri: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - software_id: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - software_version: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - contacts: - type: array - nullable: true - items: - type: string - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - jwks: - type: object - nullable: true - additionalProperties: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - jwks_uri: - type: string - nullable: true - description: "**REJECTED IF PRESENT.** Unsupported RFC 7591 metadata for this public-client phase." - - OAuthRegisterResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] RFC 7591 §3.2.1 successful registration response." - required: - - client_id - - client_id_issued_at - - redirect_uris - - grant_types - - response_types - - token_endpoint_auth_method - - application_type - properties: - client_id: - type: string - description: Server-generated client_id. - client_id_issued_at: - type: integer - format: int64 - description: Unix timestamp (seconds) when the client was registered. - client_name: - type: string - redirect_uris: - type: array - items: - type: string - grant_types: - type: array - items: - type: string - response_types: - type: array - items: - type: string - token_endpoint_auth_method: - type: string - enum: [none] - application_type: - type: string - enum: [native, web] - - OAuthRegisterError: - type: object - x-runtime: [cloud] - description: "[cloud-only] RFC 7591 §3.2.2 error response." - required: - - error - properties: - error: - type: string - enum: [invalid_redirect_uri, invalid_client_metadata] - error_description: - type: string - nullable: true - - BillingBalance: - type: object - x-runtime: [cloud] - description: "[cloud-only] Current credit balance and usage summary." - required: - - credits_remaining - properties: - credits_remaining: - type: integer - description: Available credits - credits_used: - type: integer - description: Credits used in current billing period - credits_total: - type: integer - description: Total credits allocated in current period - - BillingEvent: - type: object - x-runtime: [cloud] - description: "[cloud-only] A billing event (charge, credit, refund)." - required: - - id - - type - - amount - - created_at - properties: - id: - type: string - type: - type: string - enum: [charge, credit, refund, topup, subscription] - amount: - type: integer - description: Amount in credits - description: - type: string - job_id: - type: string - format: uuid - nullable: true - created_at: - type: string - format: date-time - - BillingEventList: - type: object - x-runtime: [cloud] - description: "[cloud-only] Paginated list of billing events." - required: - - events - - total - - has_more - properties: - events: - type: array - items: - $ref: "#/components/schemas/BillingEvent" - total: - type: integer - has_more: - type: boolean - - BillingOp: - type: object - x-runtime: [cloud] - description: "[cloud-only] A billing operation record." - required: - - id - - status - properties: - id: - type: string - status: - type: string - enum: [pending, completed, failed] - type: - type: string - amount: - type: integer - created_at: - type: string - format: date-time - completed_at: - type: string - format: date-time - nullable: true - - BillingPlan: - type: object - x-runtime: [cloud] - description: "[cloud-only] A subscription plan with pricing details." - required: - - id - - name - properties: - id: - type: string - name: - type: string - description: - type: string - credits_per_month: - type: integer - price_cents: - type: integer - description: Monthly price in cents (USD) - currency: - type: string - default: usd - features: - type: array - items: - type: string - description: List of plan features - - BillingStatus: - type: string - x-runtime: [cloud] - description: "[cloud-only] Overall billing/payment lifecycle status." - enum: - - awaiting_payment_method - - pending_payment - - paid - - payment_failed - - inactive - - BillingSubscription: - type: object - x-runtime: [cloud] - description: "[cloud-only] Active subscription details." - required: - - id - - status - - plan_id - properties: - id: - type: string - status: - type: string - enum: [active, cancelled, past_due, trialing] - plan_id: - type: string - plan_name: - type: string - current_period_start: - type: string - format: date-time - current_period_end: - type: string - format: date-time - cancel_at_period_end: - type: boolean - - SubscriptionPreview: - type: object - x-runtime: [cloud] - description: "[cloud-only] Preview of a subscription change including prorations." - properties: - plan_id: - type: string - plan_name: - type: string - amount_due: - type: integer - description: Amount due in cents - proration_amount: - type: integer - description: Proration adjustment in cents - currency: - type: string - next_billing_date: - type: string - format: date-time - - Workspace: - type: object - x-runtime: [cloud] - description: "[cloud-only] A cloud workspace for team collaboration." - required: - - id - - name - properties: - id: - type: string - name: - type: string - type: - type: string - enum: - - personal - - team - description: Workspace type (personal vs. team). - owner_id: - type: string - member_count: - type: integer - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - WorkspaceMember: - type: object - x-runtime: [cloud] - description: "[cloud-only] A member of a cloud workspace." - required: - - user_id - - role - properties: - user_id: - type: string - email: - type: string - format: email - display_name: - type: string - avatar_url: - type: string - format: uri - role: - type: string - enum: [owner, admin, member] - joined_at: - type: string - format: date-time - - WorkspaceInvite: - type: object - x-runtime: [cloud] - description: "[cloud-only] A pending workspace invitation." - required: - - id - - email - - role - properties: - id: - type: string - email: - type: string - format: email - role: - type: string - enum: [admin, member] - invited_by: - type: string - created_at: - type: string - format: date-time - expires_at: - type: string - format: date-time - - WorkspaceApiKey: - type: object - x-runtime: [cloud] - description: "[cloud-only] A workspace API key (secret value redacted)." - required: - - id - - name - - description - properties: - id: - type: string - name: - type: string - description: - type: string - maxLength: 5000 - description: User-provided description of the key's purpose. Always present in responses; empty string when no description was supplied on create. - prefix: - type: string - description: First few characters of the key for identification - created_at: - type: string - format: date-time - last_used_at: - type: string - format: date-time - nullable: true - created_by: - type: string - - WorkspaceApiKeyCreated: - type: object - x-runtime: [cloud] - description: "[cloud-only] A newly created workspace API key, including the full secret value (shown only once)." - required: - - id - - name - - description - - key - properties: - id: - type: string - name: - type: string - description: - type: string - maxLength: 5000 - description: User-provided description of the key's purpose. Always present in responses; empty string when no description was supplied on create. - key: - type: string - description: Full API key value (only returned on creation) - prefix: - type: string - created_at: - type: string - format: date-time - - CloudUser: - type: object - x-runtime: [cloud] - description: "[cloud-only] A cloud-authenticated user profile." - required: - - id - - email - properties: - id: - type: string - email: - type: string - format: email - display_name: - type: string - avatar_url: - type: string - format: uri - created_at: - type: string - format: date-time - - SecretMeta: - type: object - x-runtime: [cloud] - description: "[cloud-only] Metadata for a stored secret (value is never returned)." - required: - - id - - name - properties: - id: - type: string - name: - type: string - provider: - type: string - description: "[cloud-only] Provider identifier (e.g., huggingface, civitai)." - x-runtime: [cloud] - last_used_at: - type: string - format: date-time - description: "[cloud-only] When the secret was last used for decryption." - x-runtime: [cloud] - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - UpdateSecretRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] Request body for updating an existing user secret." - properties: - name: - type: string - description: New name for the secret - secret_value: - type: string - description: New secret value (API key, token, etc.) - - CreateSessionResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Response after creating a session cookie." - required: - - success - properties: - success: - type: boolean - expiresIn: - type: integer - description: Session expiration time in seconds. - - DeleteSessionResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Response after deleting a session cookie." - required: - - success - properties: - success: - type: boolean - - CreateHubProfileRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] Request body for creating a new Hub profile." - required: - - workspace_id - - username - properties: - workspace_id: - type: string - username: - type: string - description: Unique URL-safe slug. Immutable after creation. - display_name: - type: string - description: - type: string - avatar_token: - type: string - website_urls: - type: array - items: - type: string - - PublishHubWorkflowRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] Request body for publishing or updating a workflow on the Hub." - required: - - username - - name - - workflow_filename - - asset_ids - properties: - username: - type: string - name: - type: string - workflow_filename: - type: string - asset_ids: - type: array - items: - type: string - description: - type: string - tags: - type: array - items: - type: string - models: - type: array - items: - type: string - custom_nodes: - type: array - items: - type: string - tutorial_url: - type: string - metadata: - type: object - additionalProperties: true - thumbnail_type: - type: string - enum: [image, video, image_comparison] - thumbnail_token_or_url: - type: string - thumbnail_comparison_token_or_url: - type: string - sample_image_tokens_or_urls: - type: array - items: - type: string - - HubWorkflowDetail: - type: object - x-runtime: [cloud] - description: "[cloud-only] Full Hub workflow detail including versions, assets, and statistics." - required: - - share_id - - workflow_id - - name - - workflow_json - - assets - - profile - - status - properties: - share_id: - type: string - workflow_id: - type: string - name: - type: string - status: - type: string - enum: [pending, approved, rejected, deprecated] - description: - type: string - thumbnail_type: - type: string - enum: [image, video, image_comparison] - thumbnail_url: - type: string - thumbnail_comparison_url: - type: string - tutorial_url: - type: string - metadata: - type: object - additionalProperties: true - sample_image_urls: - type: array - items: - type: string - publish_time: - type: string - format: date-time - nullable: true - workflow_json: - type: object - additionalProperties: true - assets: - type: array - items: - $ref: "#/components/schemas/AssetInfo" - profile: - $ref: "#/components/schemas/HubProfile" - - AssetInfo: - type: object - x-runtime: [cloud] - description: "[cloud-only] Lightweight asset reference used in workflow publishing payloads." - required: - - id - - filename - properties: - id: - type: string - filename: - type: string - mime_type: - type: string - size_bytes: - type: integer - format: int64 - - BulkRevokeAPIKeysResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Response after bulk-revoking API keys for a workspace member." - required: - - revoked_count - properties: - revoked_count: - type: integer - minimum: 0 - - CreateWorkflowVersionRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] Request body for creating a new version of a saved workflow." - required: - - base_version - - workflow_json - properties: - base_version: - type: integer - description: Version number this change is based on (for optimistic concurrency). - workflow_json: - type: object - additionalProperties: true - - WorkflowVersionResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Metadata for a single workflow version." - required: - - id - - version - - latest_version - - created_by - - created_at - properties: - id: - type: string - version: - type: integer - latest_version: - type: integer - created_by: - type: string - created_at: - type: string - format: date-time - - WorkflowPublishInfo: - type: object - x-runtime: [cloud] - description: "[cloud-only] Publishing metadata for a workflow shared to the Hub." - required: - - workflow_id - - share_id - - listed - - assets - properties: - workflow_id: - type: string - share_id: - type: string - publish_time: - type: string - format: date-time - nullable: true - listed: - type: boolean - assets: - type: array - items: - $ref: "#/components/schemas/AssetInfo" - - TaskEntry: - type: object - x-runtime: [cloud] - description: "[cloud-only] Task data for list views." - required: - - id - - task_name - - status - - create_time - properties: - id: - type: string - format: uuid - task_name: - type: string - status: - type: string - enum: [created, running, completed, failed] - create_time: - type: string - format: date-time - started_at: - type: string - format: date-time - completed_at: - type: string - format: date-time - - TaskResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Full task details including payload and result." - required: - - id - - idempotency_key - - task_name - - payload - - status - - create_time - - update_time - properties: - id: - type: string - format: uuid - idempotency_key: - type: string - task_name: - type: string - payload: - type: object - additionalProperties: true - status: - type: string - enum: [created, running, completed, failed] - result: - type: object - additionalProperties: true - create_time: - type: string - format: date-time - update_time: - type: string - format: date-time - started_at: - type: string - format: date-time - completed_at: - type: string - format: date-time - error: - type: string - - TasksListResponse: - type: object - x-runtime: [cloud] - description: "[cloud-only] Paginated list of background tasks for the authenticated user." - required: - - tasks - - pagination - properties: - tasks: - type: array - items: - $ref: "#/components/schemas/TaskEntry" - pagination: - $ref: "#/components/schemas/PaginationInfo" - - # ===== Cloud-only schemas (Comfy-Org/cloud runtime, BE-1106) ===== - AssetDownloadResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Acknowledgement of an async asset download task; clients poll GET /api/tasks/{task_id} for status.' - required: - - task_id - - status - properties: - task_id: - type: string - format: uuid - description: Task ID for tracking download progress via GET /api/tasks/{task_id} - status: - type: string - enum: - - created - - running - - completed - - failed - description: Current task status - message: - type: string - description: Human-readable message - example: Download task created. Use task_id to track progress. - - AssetMetadataResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Metadata for a remotely hosted asset resolved by URL.' - required: - - content_length - properties: - content_length: - type: integer - format: int64 - description: Size of the asset in bytes (-1 if unknown) - example: 4294967296 - content_type: - type: string - description: MIME type of the asset - example: application/octet-stream - filename: - type: string - description: Suggested filename for the asset from source - example: realistic-vision-v5.safetensors - name: - type: string - description: Display name or title for the asset from source - example: Realistic Vision v5.0 - tags: - type: array - items: - type: string - description: Tags for categorization from source - example: - - models - - checkpoint - preview_image: - type: string - description: Preview image as base64-encoded data URL - example: data:image/jpeg;base64,/9j/4AAQSkZJRg... - validation: - description: Validation results for the file - allOf: - - $ref: '#/components/schemas/ValidationResult' - - BillingBalanceResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Current credit balance and usage details for a workspace.' - required: - - amount_micros - - currency - properties: - amount_micros: - type: number - format: double - description: The total remaining balance in microamount (1/1,000,000 of the currency unit) - prepaid_balance_micros: - type: number - format: double - description: The remaining balance from prepaid commits in microamount - cloud_credit_balance_micros: - type: number - format: double - description: The remaining balance from cloud credits in microamount - pending_charges_micros: - type: number - format: double - description: The total amount of pending/unbilled charges from draft invoices in microamount - effective_balance_micros: - type: number - format: double - description: The effective balance (total balance minus pending charges). Can be negative if pending charges exceed - the balance. - currency: - type: string - example: usd - description: Currency code - - BillingPlansResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] List of available billing plans for subscription.' - required: - - plans - properties: - current_plan_slug: - type: string - description: Current plan slug if subscribed - plans: - type: array - items: - $ref: '#/components/schemas/Plan' - - BillingStatusResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Current billing and subscription status for a workspace.' - required: - - is_active - - has_funds - properties: - is_active: - type: boolean - description: Whether the workspace has an active subscription - subscription_status: - type: string - enum: - - active - - ended - - canceled - description: Subscription activity status (scheduled subscriptions are not returned) - subscription_tier: - $ref: '#/components/schemas/SubscriptionTier' - subscription_duration: - $ref: '#/components/schemas/SubscriptionDuration' - plan_slug: - type: string - description: Plan identifier (e.g., standard-monthly, team-pro-annual) - billing_status: - $ref: '#/components/schemas/BillingStatus' - has_funds: - type: boolean - description: Whether the workspace has available credits - cancel_at: - type: string - format: date-time - description: When the subscription will become inactive (if canceled) - renewal_date: - type: string - format: date-time - description: When the current billing period ends and the next one begins - - GetUserDataResponseFull: - type: array - x-runtime: [cloud] - description: '[cloud-only] List of user data file entries (each with path, size, and modification time) returned when full_info=true.' - items: - $ref: '#/components/schemas/GetUserDataResponseFullFile' - - HistoryDetailEntry: - type: object - x-runtime: [cloud] - description: '[cloud-only] History entry with full prompt data' - properties: - prompt: - type: object - description: Full prompt execution data - properties: - priority: - type: number - format: double - description: Execution priority - prompt_id: - type: string - description: The prompt ID - prompt: - type: object - description: The workflow nodes - additionalProperties: true - extra_data: - type: object - description: Additional execution data - additionalProperties: true - outputs_to_execute: - type: array - items: - type: string - description: Output nodes to execute - outputs: - type: object - description: Output data from execution (generated images, files, etc.) - additionalProperties: true - status: - type: object - description: Execution status and timeline information - additionalProperties: true - meta: - type: object - description: Metadata about the execution and nodes - additionalProperties: true - - HistoryDetailResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Detailed execution history response for a specific prompt. - - Returns a dictionary with prompt_id as key and full history data as value. - - ' - additionalProperties: - $ref: '#/components/schemas/HistoryDetailEntry' - - HistoryResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Execution history response with history array. - - Returns an object with a "history" key containing an array of history entries. - - Each entry includes prompt_id as a property along with execution data. - - ' - required: - - history - properties: - history: - type: array - description: Array of history entries ordered by creation time (newest first) - items: - $ref: '#/components/schemas/HistoryEntry' - - HubLabelInfo: - type: object - x-runtime: [cloud] - description: '[cloud-only] Metadata for a single Hub label.' - required: - - name - - display_name - - type - properties: - name: - type: string - description: Slug identifier. - display_name: - type: string - description: Human-readable display name. - description: - type: string - description: Optional description of the label. - type: - type: string - enum: - - tag - - model - - custom_node - description: Label category. - - HubLabelListResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response wrapper for the available Hub label catalog.' - required: - - labels - properties: - labels: - type: array - items: - $ref: '#/components/schemas/HubLabelInfo' - description: Available labels, optionally filtered by type. - - HubProfileSummary: - type: object - x-runtime: [cloud] - description: '[cloud-only] Abbreviated Hub profile used in workflow listings.' - required: - - username - properties: - username: - type: string - display_name: - type: string - avatar_url: - type: string - description: Public URL of the profile avatar image. - - HubWorkflowListResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Paginated list of Hub workflows matching search criteria.' - required: - - workflows - properties: - workflows: - type: array - items: - anyOf: - - $ref: '#/components/schemas/HubWorkflowSummary' - - $ref: '#/components/schemas/HubWorkflowDetail' - description: Array of HubWorkflowSummary (default) or HubWorkflowDetail (when detail=true). - next_cursor: - type: string - description: Cursor for the next page, empty if no more results. - - HubWorkflowStatus: - type: string - x-runtime: [cloud] - description: '[cloud-only] Public workflow status. NULL in the database is represented as pending in API responses.' - enum: - - pending - - approved - - rejected - - deprecated - - HubWorkflowSummary: - type: object - x-runtime: [cloud] - description: '[cloud-only] Abbreviated Hub workflow metadata used in search and listing results.' - required: - - share_id - - name - - profile - - status - properties: - share_id: - type: string - name: - type: string - status: - $ref: '#/components/schemas/HubWorkflowStatus' - description: - type: string - tags: - type: array - items: - $ref: '#/components/schemas/LabelRef' - models: - type: array - items: - $ref: '#/components/schemas/LabelRef' - custom_nodes: - type: array - items: - $ref: '#/components/schemas/LabelRef' - thumbnail_type: - type: string - enum: - - image - - video - - image_comparison - thumbnail_url: - type: string - thumbnail_comparison_url: - type: string - publish_time: - type: string - format: date-time - nullable: true - profile: - $ref: '#/components/schemas/HubProfileSummary' - metadata: - type: object - additionalProperties: true - tutorial_url: - type: string - sample_image_urls: - type: array - items: - type: string - - HubWorkflowTemplateEntry: - type: object - x-runtime: [cloud] - description: '[cloud-only] Entry in the curated workflow template gallery shown on the home page.' - required: - - name - - title - - status - properties: - name: - type: string - description: Slug identifier for the template - title: - type: string - status: - $ref: '#/components/schemas/HubWorkflowStatus' - description: - type: string - tags: - type: array - items: - type: string - models: - type: array - items: - type: string - requiresCustomNodes: - type: array - items: - type: string - thumbnailVariant: - type: string - mediaType: - type: string - mediaSubtype: - type: string - size: - type: integer - format: int64 - description: Workflow asset size in bytes. - vram: - type: integer - format: int64 - description: Approximate VRAM requirement in bytes. - usage: - type: integer - format: int64 - description: Usage count reported upstream. - searchRank: - type: integer - format: int64 - description: Search ranking score reported upstream. - isEssential: - type: boolean - description: Whether the template belongs to a module marked as essential. - openSource: - type: boolean - profile: - $ref: '#/components/schemas/HubProfileSummary' - tutorialUrl: - type: string - logos: - type: array - items: - type: object - additionalProperties: true - date: - type: string - description: Publication date in YYYY-MM-DD format - io: - type: object - properties: - inputs: - type: array - items: - type: object - additionalProperties: true - outputs: - type: array - items: - type: object - additionalProperties: true - includeOnDistributions: - type: array - items: - type: string - thumbnailUrl: - type: string - description: Public URL of the primary thumbnail - thumbnailComparisonUrl: - type: string - description: Public URL of the comparison thumbnail - shareId: - type: string - description: Share ID for linking to the hub workflow detail - extendedDescription: - type: string - description: AI-generated extended description of the workflow - metaDescription: - type: string - description: AI-generated SEO meta description (under 160 chars) - howToUse: - type: array - items: - type: string - description: AI-generated step-by-step usage instructions - suggestedUseCases: - type: array - items: - type: string - description: AI-generated suggested use cases - faqItems: - type: array - items: - type: object + default_view: + description: Default view mode + enum: + - workflow + - app + type: string + description: + description: Description of the workflow + type: string + forked_from_workflow_id: + description: ID of the source workflow if forked + type: string + forked_from_workflow_version_id: + description: ID of the source workflow version if forked + type: string + name: + description: Display name for the workflow + type: string + workflow_json: + additionalProperties: true + description: The ComfyUI workflow JSON + type: object required: - - question - - answer - properties: - question: - type: string - answer: - type: string - description: AI-generated FAQ items - contentTemplate: - type: string - description: Content template used for generation (tutorial, showcase, comparison, breakthrough) - - JobStatusResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Job status information' - properties: - id: - type: string - format: uuid - description: The job ID - status: - type: string - enum: - - waiting_to_dispatch - - pending - - in_progress - - completed - - error - - cancelled - description: Current job status - created_at: - type: string - format: date-time - description: When the job was created - updated_at: - type: string - format: date-time - description: When the job was last updated - last_state_update: - type: string - format: date-time - description: When the job status was last changed - assigned_inference: - type: string - nullable: true - description: The inference instance assigned to this job (if any) - error_message: - type: string - nullable: true - description: Error message if the job failed - required: - - id - - status - - created_at - - updated_at - - JobsListResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Paginated list of jobs for the authenticated user.' - required: - - jobs - - pagination - properties: - jobs: - type: array - description: Array of jobs ordered by specified sort field - items: - $ref: '#/components/schemas/JobEntry' - pagination: - $ref: '#/components/schemas/PaginationInfo' - - LabelRef: - type: object - x-runtime: [cloud] - description: '[cloud-only] Reference to a Hub label by ID.' - required: - - name - - display_name - properties: - name: - type: string - description: Slug identifier (e.g. "video-generation", "flux"). - display_name: - type: string - description: Human-readable display name (e.g. "Video Generation", "Flux"). - - LogsResponse: - type: array - x-runtime: [cloud] - description: '[cloud-only] System logs response' - items: - type: object - properties: - timestamp: - type: string - format: date-time - description: When the log entry was created - level: - type: string - enum: - - debug - - info - - warn - - error - description: Log level - message: - type: string - description: Log message - source: - type: string - description: Source of the log entry - metadata: + - workflow_json type: object + CreateWorkflowVersionRequest: + description: Request body for creating a new version of a saved workflow. + properties: + base_version: + description: The version number this change is based on (for optimistic concurrency) + type: integer + workflow_json: + additionalProperties: true + description: The updated ComfyUI workflow JSON + type: object + required: + - base_version + - workflow_json + type: object + ErrorResponse: + description: Standard error response with a machine-readable code and human-readable message. + properties: + code: + type: string + details: + additionalProperties: true + description: Optional open object carrying structured, machine-readable context about the error (e.g. offending field names, validation specifics). Absent for most errors; consumers must not assume any particular shape. + type: object + message: + type: string + required: + - code + - message + type: object + ExecutionError: + description: Detailed execution error information from ComfyUI + properties: + current_inputs: + additionalProperties: true + description: Input values at time of failure (empty object if not available) + type: object + current_outputs: + additionalProperties: true + description: Output values at time of failure (empty object if not available) + type: object + exception_message: + description: Human-readable error message + type: string + exception_type: + description: Python exception type (e.g., "RuntimeError") + type: string + node_id: + description: ID of the node that failed + type: string + node_type: + description: Type name of the node (e.g., "KSampler") + type: string + traceback: + description: Array of traceback lines (empty array if not available) + items: + type: string + type: array + required: + - node_id + - node_type + - exception_message + - exception_type + - traceback + - current_inputs + - current_outputs + type: object + FeedbackRequest: + description: Request to submit user feedback + properties: + content: + description: The feedback content or message + type: string + metadata: + additionalProperties: true + description: Additional metadata about the feedback + type: object + rating: + description: User's rating of ComfyUI Cloud experience (1-5 stars) + maximum: 5 + minimum: 1 + type: integer + type: + description: Type of feedback being submitted + enum: + - missing_nodes + - general + - missing_models + type: string + required: + - type + type: object + FeedbackResponse: + description: Response after submitting feedback + type: object + ForkWorkflowRequest: + description: Request body for forking an existing workflow into the user's account. + properties: + name: + description: Name for the forked workflow + type: string + source_version: + description: Version number to fork from + type: integer + required: + - source_version + type: object + GetUserDataResponseFull: + description: List of user data file entries (each with path, size, and modification time) returned when full_info=true. + items: + $ref: '#/components/schemas/GetUserDataResponseFullFile' + type: array + GetUserDataResponseFullFile: + description: Individual file entry within a full user data response. + properties: + modified: + description: UNIX timestamp of the last modification in milliseconds. + format: int64 + type: integer + path: + description: File name or path relative to the user directory. + type: string + size: + description: File size in bytes. + type: integer + type: object + GlobalSubgraphData: + description: Full data for a global subgraph blueprint + properties: + data: + description: The full subgraph JSON data as a string + type: string + info: + description: Additional information about the subgraph + properties: + node_pack: + description: The node pack/module that provides this subgraph + type: string + required: + - node_pack + type: object + name: + description: Display name of the subgraph blueprint + type: string + source: + description: Source type of the subgraph - "templates" for workflow templates or "custom_node" for custom node subgraphs + type: string + required: + - source + - name + - info + - data + type: object + GlobalSubgraphInfo: + description: Metadata for a global subgraph blueprint (without full data) + properties: + data: + description: The full subgraph JSON data (may be empty in list view) + type: string + info: + description: Additional information about the subgraph + properties: + node_pack: + description: The node pack/module that provides this subgraph + type: string + required: + - node_pack + type: object + name: + description: Display name of the subgraph blueprint + type: string + source: + description: Source type of the subgraph - "templates" for workflow templates or "custom_node" for custom node subgraphs + type: string + required: + - source + - name + - info + type: object + HistoryDetailEntry: + description: History entry with full prompt data + properties: + meta: + additionalProperties: true + description: Metadata about the execution and nodes + type: object + outputs: + additionalProperties: true + description: Output data from execution (generated images, files, etc.) + type: object + prompt: + description: Full prompt execution data + properties: + extra_data: + additionalProperties: true + description: Additional execution data + type: object + outputs_to_execute: + description: Output nodes to execute + items: + type: string + type: array + priority: + description: Execution priority + format: double + type: number + prompt: + additionalProperties: true + description: The workflow nodes + type: object + prompt_id: + description: The prompt ID + type: string + type: object + status: + additionalProperties: true + description: Execution status and timeline information + type: object + type: object + HistoryDetailResponse: + additionalProperties: + $ref: '#/components/schemas/HistoryDetailEntry' + description: | + Detailed execution history response for a specific prompt. + Returns a dictionary with prompt_id as key and full history data as value. + type: object + HistoryEntry: + description: History entry with prompt_id and execution data + properties: + create_time: + description: Job creation timestamp (Unix timestamp in milliseconds) + format: int64 + type: integer + meta: + additionalProperties: true + description: Metadata about the execution and nodes + type: object + outputs: + additionalProperties: true + description: Output data from execution (generated images, files, etc.) + type: object + prompt: + description: Filtered prompt execution data (lightweight format) + properties: + extra_data: + additionalProperties: true + description: Additional execution data (workflow removed from extra_pnginfo) + type: object + priority: + description: Execution priority + format: double + type: number + prompt_id: + description: The prompt ID + type: string + type: object + prompt_id: + description: Unique identifier for this prompt execution + type: string + status: + additionalProperties: true + description: Execution status and timeline information + type: object + workflow_id: + description: UUID identifying the workflow graph definition + type: string + required: + - prompt_id + type: object + HistoryManageRequest: + additionalProperties: false + description: Request to manage history operations + properties: + clear: + description: If true, clear all history for the authenticated user + type: boolean + delete: + description: Array of job IDs to delete from history + items: + type: string + type: array + type: object + HistoryResponse: + description: | + Execution history response with history array. + Returns an object with a "history" key containing an array of history entries. + Each entry includes prompt_id as a property along with execution data. + properties: + history: + description: Array of history entries ordered by creation time (newest first) + items: + $ref: '#/components/schemas/HistoryEntry' + type: array + required: + - history + type: object + JobCancelResponse: + description: Response for POST /api/jobs/{job_id}/cancel. Returned on both fresh cancels and idempotent no-ops. + properties: + cancelled: + description: | + True when a cancel event was successfully dispatched by this call. + False when the job was already in a terminal or cancelling state, + in which case the call is a no-op (still 200 — idempotent). + type: boolean + required: + - cancelled + type: object + JobDetailResponse: + description: Full job details including workflow and outputs + properties: + create_time: + description: Job creation timestamp (Unix timestamp in milliseconds) + format: int64 + type: integer + execution_error: + allOf: + - $ref: '#/components/schemas/ExecutionError' + description: Detailed execution error from ComfyUI (only for failed jobs with structured error data) + execution_meta: + additionalProperties: true + description: Node-level execution metadata (only for terminal states) + type: object + execution_status: + additionalProperties: true + description: ComfyUI execution status and timeline (only for terminal states) + type: object + id: + description: Unique job identifier + format: uuid + type: string + outputs: + additionalProperties: true + description: Full outputs object from ComfyUI (only for terminal states) + type: object + outputs_count: + description: Total number of output files (omitted for non-terminal states) + type: integer + preview_output: + additionalProperties: true + description: Primary preview output (only for terminal states) + type: object + status: + description: User-friendly job status + enum: + - pending + - in_progress + - completed + - failed + - cancelled + type: string + update_time: + description: Last update timestamp (Unix timestamp in milliseconds) + format: int64 + type: integer + workflow: + additionalProperties: true + description: | + Full ComfyUI workflow (10-100KB, omitted if not available). + + Sensitive credentials are redacted before the response is returned: + `extra_data.api_key_comfy_org`, when present, is replaced with the + literal string `"[REDACTED]"`. The field is preserved (not removed) + so existence checks still pass, but the value is not usable. + type: object + workflow_id: + description: UUID identifying the workflow graph definition + type: string + required: + - id + - status + - create_time + - update_time + type: object + JobEntry: + description: Lightweight job data for list views (workflow and full outputs excluded) + properties: + create_time: + description: Job creation timestamp (Unix timestamp in milliseconds) + format: int64 + type: integer + execution_end_time: + description: Workflow execution completion timestamp (Unix milliseconds, only present for terminal states) + format: int64 + type: integer + execution_error: + allOf: + - $ref: '#/components/schemas/ExecutionError' + description: Detailed execution error from ComfyUI (only for failed jobs with structured error data) + execution_start_time: + description: Workflow execution start timestamp (Unix milliseconds, only present for terminal states) + format: int64 + type: integer + id: + description: Unique job identifier + format: uuid + type: string + outputs_count: + description: Total number of output files (omitted for non-terminal states) + type: integer + preview_output: + additionalProperties: true + description: Primary preview output (only present for terminal states) + type: object + status: + description: User-friendly job status + enum: + - pending + - in_progress + - completed + - failed + - cancelled + type: string + workflow_id: + description: UUID identifying the workflow graph definition + type: string + required: + - id + - status + - create_time + type: object + JobStatusResponse: + description: Job status information + properties: + assigned_inference: + description: The inference instance assigned to this job (if any) + nullable: true + type: string + created_at: + description: When the job was created + format: date-time + type: string + error_message: + description: Error message if the job failed + nullable: true + type: string + id: + description: The job ID + format: uuid + type: string + last_state_update: + description: When the job status was last changed + format: date-time + type: string + status: + description: Current job status + enum: + - waiting_to_dispatch + - pending + - in_progress + - completed + - error + - cancelled + type: string + updated_at: + description: When the job was last updated + format: date-time + type: string + required: + - id + - status + - created_at + - updated_at + type: object + JobsListResponse: + description: Paginated list of jobs for the authenticated user. + properties: + jobs: + description: Array of jobs ordered by specified sort field + items: + $ref: '#/components/schemas/JobEntry' + type: array + pagination: + $ref: '#/components/schemas/PaginationInfo' + required: + - jobs + - pagination + type: object + ListAssetsResponse: + description: Paginated list of assets belonging to the authenticated user. + properties: + assets: + description: List of assets matching the query + items: + $ref: '#/components/schemas/Asset' + type: array + has_more: + description: Whether more assets are available beyond this page + type: boolean + next_cursor: + description: | + Opaque cursor to pass as the `after` query parameter to fetch the + next page. Omitted from the response when there are no more results. + type: string + total: + description: Total number of assets matching the filters + type: integer + required: + - assets + - total + - has_more + type: object + ListTagsResponse: + description: Paginated list of available asset tags. + properties: + has_more: + description: Whether more tags are available + type: boolean + tags: + description: List of tags + items: + $ref: '#/components/schemas/TagInfo' + type: array + total: + description: Total number of tags + type: integer + required: + - tags + - total + - has_more + type: object + ModelFile: + description: Represents a model file with metadata + properties: + name: + description: The filename of the model + example: model.safetensors + type: string + pathIndex: + description: Index of the path where this model is located + example: 0 + type: integer + required: + - name + - pathIndex + type: object + ModelFolder: + description: Represents a folder containing models + properties: + folders: + description: List of paths where models of this type are stored + example: + - checkpoints + items: + type: string + type: array + name: + description: The name of the model folder + example: checkpoints + type: string + required: + - name + - folders + type: object + NodeInfo: + description: Metadata describing a single ComfyUI node type and its inputs/outputs. + properties: + api_node: + description: Whether this is an API node + type: boolean + category: + description: Category of the node + type: string + deprecated: + description: Whether the node is deprecated + type: boolean + description: + description: Description of the node + type: string + display_name: + description: Display name of the node + type: string + experimental: + description: Whether the node is experimental + type: boolean + input: + additionalProperties: true + description: Input specifications for the node + type: object + input_order: + additionalProperties: + items: + type: string + type: array + description: Order of inputs for display + type: object + name: + description: Internal name of the node + type: string + output: + description: Output types of the node + items: + type: string + type: array + output_is_list: + description: Whether each output is a list + items: + type: boolean + type: array + output_name: + description: Names of the outputs + items: + type: string + type: array + output_node: + description: Whether this is an output node + type: boolean + output_tooltips: + description: Tooltips for outputs + items: + type: string + type: array + python_module: + description: Python module implementing the node + type: string + type: object + PaginationInfo: + description: Offset/limit-based pagination metadata included in list responses. + properties: + has_more: + description: Whether more items are available beyond this page + type: boolean + limit: + description: Items per page + minimum: 1 + type: integer + offset: + description: Current offset (0-based) + minimum: 0 + type: integer + total: + description: Total number of items matching filters + minimum: 0 + type: integer + required: + - offset + - limit + - total + - has_more + type: object + PromptErrorResponse: additionalProperties: true - description: Additional log metadata + description: Error response for ComfyUI prompt execution. + type: object + PromptInfo: + description: Metadata about the currently running and queued prompts. + properties: + exec_info: + properties: + queue_remaining: + description: Number of items remaining in the queue + type: integer + type: object + type: object + PromptRequest: + description: Request body for submitting a ComfyUI workflow prompt for execution. + properties: + extra_data: + additionalProperties: true + description: Extra data to be associated with the prompt + type: object + front: + description: If true, adds the prompt to the front of the queue + type: boolean + number: + description: Priority number for the queue (lower numbers have higher priority) + type: number + partial_execution_targets: + description: List of node names to execute + items: + type: string + type: array + prompt: + additionalProperties: true + description: The workflow graph to execute + type: object + workflow_id: + description: UUID identifying the cloud workflow entity to associate with this job + type: string + workflow_version_id: + description: UUID identifying the workflow version to associate with this job + type: string + required: + - prompt + type: object + PromptResponse: + description: Response returned after successfully queuing a workflow prompt. + properties: + node_errors: + additionalProperties: true + description: Any errors in the nodes of the prompt + type: object + number: + description: Priority number in the queue + type: number + prompt_id: + description: Unique identifier for the prompt execution + format: uuid + type: string + type: object + PublishWorkflowAssetsRequest: + description: Request body for publishing workflow assets to the Hub. + properties: + asset_ids: + description: IDs of assets (inputs and models) to snapshot. + items: + type: string + type: array + required: + - asset_ids + type: object + PublishedWorkflowDetail: + description: Full detail of a publicly published workflow on the Hub. + properties: + assets: + description: Published assets with their library status for the caller. + items: + $ref: '#/components/schemas/AssetInfo' + type: array + listed: + type: boolean + name: + description: Human-readable workflow name. + type: string + publish_time: + format: date-time + nullable: true + type: string + share_id: + type: string + workflow_id: + type: string + workflow_json: + additionalProperties: true + description: The workflow JSON content at publish time. + type: object + required: + - share_id + - workflow_id + - name + - listed + - workflow_json + - assets + type: object + QueueInfo: + description: Queue information with pending and running jobs + properties: + queue_pending: + description: Array of pending job items (ordered by creation time, oldest first) + items: + description: | + Queue item tuple format: [job_number, prompt_id, workflow_json, output_node_ids, metadata] + - [0] job_number (integer): Position in queue (1-based) + - [1] prompt_id (string): Job UUID + - [2] workflow_json (object): Full ComfyUI workflow + - [3] output_node_ids (array): Node IDs to return results from + - [4] metadata (object): Contains {create_time: } + items: {} + maxItems: 5 + minItems: 5 + type: array + type: array + queue_running: + description: Array of currently running job items + items: + description: | + Queue item tuple format: [job_number, prompt_id, workflow_json, output_node_ids, metadata] + - [0] job_number (integer): Position in queue (1-based) + - [1] prompt_id (string): Job UUID + - [2] workflow_json (object): Full ComfyUI workflow + - [3] output_node_ids (array): Node IDs to return results from + - [4] metadata (object): Contains {create_time: } + items: {} + maxItems: 5 + minItems: 5 + type: array + type: array + type: object + QueueManageRequest: + additionalProperties: false + description: Request to manage queue operations + properties: + clear: + description: If true, clear all pending jobs from the queue + type: boolean + delete: + description: Array of PENDING job IDs to cancel + items: + type: string + type: array + type: object + QueueManageResponse: + description: Response after a queue management action (delete or clear). + properties: + cleared: + description: Whether the queue was cleared + type: boolean + deleted: + description: Array of job IDs that were successfully cancelled + items: + type: string + type: array + type: object + SystemStatsResponse: + description: System statistics response + properties: + devices: + items: + properties: + name: + description: Device name + type: string + type: + description: Device type + type: string + vram_free: + description: Free VRAM in bytes + type: number + vram_total: + description: Total VRAM in bytes + type: number + required: + - name + - type + type: object + type: array + system: + properties: + argv: + description: Command line arguments + items: + type: string + type: array + cloud_version: + description: Cloud ingest service version (commit hash) + type: string + comfyui_frontend_version: + description: ComfyUI frontend version (commit hash or tag) + type: string + comfyui_version: + description: ComfyUI version + type: string + embedded_python: + description: Whether using embedded Python + type: boolean + os: + description: Operating system + type: string + python_version: + description: Python version + type: string + pytorch_version: + description: PyTorch version + type: string + ram_free: + description: Free RAM in bytes + type: number + ram_total: + description: Total RAM in bytes + type: number + workflow_templates_version: + description: Workflow templates version + type: string + required: + - os + - python_version + - embedded_python + - comfyui_version + - pytorch_version + - argv + - ram_total + - ram_free + type: object + required: + - system + - devices + type: object + TagInfo: + description: Metadata for a single tag that can be applied to assets. + properties: + count: + description: Number of assets using this tag + type: integer + name: + description: Tag name + type: string + required: + - name + - count + type: object + TagsModificationResponse: + description: Response after adding, updating, or removing tags on an asset. + properties: + added: + description: Tags that were successfully added (for add operation) + items: + type: string + type: array + already_present: + description: Tags that were already present (for add operation) + items: + type: string + type: array + not_present: + description: Tags that were not present (for remove operation) + items: + type: string + type: array + removed: + description: Tags that were successfully removed (for remove operation) + items: + type: string + type: array + total_tags: + description: All tags on the asset after the operation + items: + type: string + type: array + required: + - total_tags + type: object + TaskEntry: + description: Task data for list views + properties: + completed_at: + description: When task completed or failed (null if not finished) + format: date-time + type: string + create_time: + description: Task creation timestamp + format: date-time + type: string + id: + description: Unique task identifier + format: uuid + type: string + started_at: + description: When task execution started (null if not started) + format: date-time + type: string + status: + description: Current task status + enum: + - created + - running + - completed + - failed + type: string + task_name: + description: Task type name (e.g., model_upload) + type: string + required: + - id + - task_name + - status + - create_time + type: object + TaskResponse: + description: Full task details including payload and result + properties: + completed_at: + description: When task completed or failed (null if not finished) + format: date-time + type: string + create_time: + description: Task creation timestamp + format: date-time + type: string + error_message: + description: Error message on failure (null if not failed) + type: string + id: + description: Unique task identifier + format: uuid + type: string + idempotency_key: + description: Caller-provided key for idempotent task creation + type: string + payload: + additionalProperties: true + description: Task input data + type: object + result: + additionalProperties: true + description: Task output data (null if not completed) + type: object + started_at: + description: When task execution started (null if not started) + format: date-time + type: string + status: + description: Current task status + enum: + - created + - running + - completed + - failed + type: string + task_name: + description: Task type name (e.g., model_upload) + type: string + update_time: + description: Task last update timestamp + format: date-time + type: string + required: + - id + - idempotency_key + - task_name + - payload + - status + - create_time + - update_time + type: object + TasksListResponse: + description: Paginated list of background tasks for the authenticated user. + properties: + pagination: + $ref: '#/components/schemas/PaginationInfo' + tasks: + description: Array of tasks ordered by create_time + items: + $ref: '#/components/schemas/TaskEntry' + type: array + required: + - tasks + - pagination + type: object + UpdateWorkflowRequest: + description: Request body for updating an existing saved workflow. + properties: + default_view: + description: New default view mode + enum: + - workflow + - app + type: string + description: + description: New description + type: string + name: + description: New display name + type: string + type: object + UserDataResponseFull: + description: User data listing entry with file metadata (path, size, modification time). + properties: + modified: + description: UNIX timestamp of the last modification in milliseconds. + format: int64 + type: integer + path: + type: string + size: + type: integer + type: object + UserResponse: + description: User information response + properties: + id: + description: Firebase UID of the authenticated user + type: string + status: + description: User status (always "active" for authenticated users) + type: string + required: + - id + - status + type: object + WorkflowForkedFrom: + description: Reference to the parent workflow from which this workflow was forked. + properties: + workflow_id: + type: string + workflow_version_id: + type: string + type: object + WorkflowListResponse: + description: Paginated list of saved workflows. + properties: + data: + items: + $ref: '#/components/schemas/WorkflowResponse' + type: array + pagination: + $ref: '#/components/schemas/PaginationInfo' + required: + - data + - pagination + type: object + WorkflowPublishInfo: + description: Publishing metadata for a workflow shared to the Hub. + properties: + assets: + description: Published assets (inputs and models). + items: + $ref: '#/components/schemas/AssetInfo' + type: array + listed: + type: boolean + publish_time: + format: date-time + nullable: true + type: string + share_id: + type: string + workflow_id: + type: string + required: + - workflow_id + - share_id + - listed + - assets + type: object + WorkflowResponse: + description: Full workflow entity including metadata and version history. + properties: + created_at: + format: date-time + type: string + created_by: + type: string + default_view: + enum: + - workflow + - app + type: string + description: + type: string + forked_from: + $ref: '#/components/schemas/WorkflowForkedFrom' + id: + type: string + latest_version: + type: integer + name: + type: string + updated_at: + format: date-time + type: string + required: + - id + - latest_version + - created_by + - created_at + - updated_at + type: object + WorkflowVersionContentResponse: + description: Full workflow version including the serialized workflow JSON. + properties: + created_at: + format: date-time + type: string + created_by: + type: string + dependency_asset_ids: + items: + type: string + type: array + id: + type: string + version: + type: integer + workflow_json: + additionalProperties: true + type: object + required: + - id + - version + - workflow_json + - created_by + - created_at + type: object + WorkflowVersionResponse: + description: Metadata for a single workflow version. + properties: + created_at: + format: date-time + type: string + created_by: + type: string + id: + type: string + latest_version: + type: integer + version: + type: integer + required: + - id + - version + - latest_version + - created_by + - created_at + type: object + securitySchemes: + ApiKeyAuth: + description: | + API key authentication. Keys are prefixed with 'comfyui-' and can be + generated from user account settings. Example: 'comfyui-abc123...' + in: header + name: X-API-Key + type: apiKey + BearerAuth: + bearerFormat: JWT + description: | + Firebase JWT token authentication. Obtain a token by authenticating + with Firebase and pass it in the Authorization header. + scheme: bearer + type: http + CookieAuth: + description: | + Session cookie authentication. Set automatically after successful + login via the /api/auth/session endpoint. + in: cookie + name: session + type: apiKey +info: + description: | + API for ComfyUI - A powerful and modular UI for Stable Diffusion. - Member: - type: object - x-runtime: [cloud] - description: '[cloud-only] Workspace member with profile and role information.' - required: - - id - - name - - email - - role - - joined_at - properties: - id: - type: string - description: User ID - name: - type: string - description: User's display name - email: - type: string - format: email - description: User's email address - role: - type: string - enum: - - owner - - member - description: User's role in the workspace - joined_at: - type: string - format: date-time - description: When the user joined the workspace + This API allows you to interact with ComfyUI programmatically, including: + - Retrieving prompt information + - Retrieving node information + license: + name: GNU General Public License v3.0 + url: https://github.com/Comfy-Org/ComfyUI/blob/master/LICENSE + title: ComfyUI API + version: 1.0.0 +openapi: 3.0.3 +paths: + /api/assets: + get: + description: | + Retrieves a paginated list of assets belonging to the authenticated user. + Supports filtering by tags, name, metadata, and sorting options. + operationId: listAssets + parameters: + - description: Filter assets that have ALL of these tags + explode: false + in: query + name: include_tags + schema: + items: + type: string + type: array + style: form + - description: Exclude assets that have ANY of these tags + explode: false + in: query + name: exclude_tags + schema: + items: + type: string + type: array + style: form + - description: Filter assets where name contains this substring (case-insensitive) + in: query + name: name_contains + schema: + type: string + - description: JSON object for filtering by metadata fields + in: query + name: metadata_filter + schema: + type: string + - description: Maximum number of assets to return (1-500) + in: query + name: limit + schema: + default: 20 + maximum: 500 + minimum: 1 + type: integer + - description: Number of assets to skip for pagination + in: query + name: offset + schema: + default: 0 + minimum: 0 + type: integer + - description: Field to sort by + in: query + name: sort + schema: + default: created_at + enum: + - name + - created_at + - updated_at + - size + - last_access_time + type: string + - description: Sort order + in: query + name: order + schema: + default: desc + enum: + - asc + - desc + type: string + - description: Whether to include public/shared assets in results + in: query + name: include_public + schema: + default: true + type: boolean + - description: Filter assets by exact content hash. Preferred over asset_hash. + in: query + name: hash + schema: + type: string + - deprecated: true + description: 'Deprecated: use hash instead. Filter assets by exact content hash.' + in: query + name: asset_hash + schema: + type: string + - description: | + Opaque cursor for keyset pagination. Pass the `next_cursor` value + from the previous response to fetch the next page. When provided, + `offset` is ignored. Cursor pagination is only supported with + `sort` values `created_at`, `updated_at`, `name`, or `size`; + requests combining `after` with other sort fields return 400. + The cursor must have been minted under the same `sort` value used + in the follow-up request. + in: query + name: after + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ListAssetsResponse' + description: Success - Assets returned + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: List user assets + tags: + - file + post: + description: | + Uploads a new asset to the system with associated metadata. + Supports two upload methods: + 1. Direct file upload (multipart/form-data) + 2. URL-based upload (application/json with source: "url") - OAuthRegisterBadRequestResponse: - x-runtime: [cloud] - description: "[cloud-only] Union of the two 400 shapes /oauth/register can emit. `OAuthRegisterError` is the handler-shaped\ - \ RFC 7591 \xA73.2.2 error; `BindingErrorResponse` is the strict-server binding-layer error fired when the request body\ - \ fails OpenAPI-schema validation before the handler runs.\n" - oneOf: - - $ref: '#/components/schemas/OAuthRegisterError' - - $ref: '#/components/schemas/BindingErrorResponse' + If an asset with the same hash already exists, returns the existing asset. + operationId: uploadAsset + requestBody: + content: + application/json: + schema: + properties: + name: + description: Display name for the asset (used to determine file extension) + type: string + preview_id: + description: Optional preview asset ID + format: uuid + type: string + tags: + description: Freeform tags for the asset. Common types include "models", "input", "output", and "temp", but any tag can be used in any order. + items: + type: string + type: array + url: + description: HTTP/HTTPS URL to download the asset from + format: uri + type: string + user_metadata: + additionalProperties: true + description: Custom metadata to store with the asset + type: object + required: + - url + - name + type: object + multipart/form-data: + schema: + properties: + file: + description: The asset file to upload + format: binary + type: string + id: + description: Optional asset ID for idempotent creation. If provided and asset exists, returns existing asset. + format: uuid + type: string + mime_type: + description: MIME type of the asset (e.g., "image/png", "video/mp4") + type: string + name: + description: Display name for the asset + type: string + preview_id: + description: Optional preview asset ID. If not provided, images will use their own ID as preview. + format: uuid + type: string + tags: + description: Freeform tags for the asset. Common types include "models", "input", "output", and "temp", but any tag can be used in any order. + items: + type: string + type: array + user_metadata: + description: Custom JSON metadata as a string + type: string + required: + - file + type: object + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/AssetCreated' + description: Asset created successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request (bad file, invalid URL, invalid content type, etc.) + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Source URL requires authentication or access denied + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Source URL not found + "413": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: File too large + "415": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unsupported media type + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Download failed due to network error or timeout + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Upload a new asset + tags: + - file + /api/assets/{id}: + delete: + description: Deletes the asset record. + operationId: deleteAsset + parameters: + - description: Asset ID + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + "204": + description: Asset record deleted successfully + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset not found + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset cannot be deleted because it is referenced by another resource (e.g., workflow version) + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Delete asset + tags: + - file + get: + description: Retrieves detailed information about a specific asset + operationId: getAssetById + parameters: + - description: Asset ID + in: path + name: id + required: true + schema: + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Asset' + description: Asset details retrieved successfully + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get asset details + tags: + - file + put: + description: | + Updates an asset's metadata. At least one field must be provided. + Only name, mime_type, preview_id, and user_metadata can be updated. + For tag management, use the dedicated PUT /api/assets/{id}/tags endpoint. + operationId: updateAsset + parameters: + - description: Asset ID + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/json: + schema: + minProperties: 1 + properties: + mime_type: + description: Updated MIME type of the asset + type: string + name: + description: New display name for the asset + type: string + preview_id: + description: Updated preview asset ID + format: uuid + type: string + user_metadata: + additionalProperties: true + description: Updated custom metadata + type: object + type: object + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/AssetUpdated' + description: Asset updated successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request (no fields provided) + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Update asset metadata + tags: + - file + /api/assets/{id}/tags: + delete: + description: Removes one or more tags from an existing asset + operationId: removeAssetTags + parameters: + - description: Asset ID + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/json: + schema: + properties: + tags: + description: Tags to remove from the asset + items: + type: string + minItems: 1 + type: array + required: + - tags + type: object + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/TagsModificationResponse' + description: Tags removed successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset not found + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error (e.g., reserved tag) + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Remove tags from asset + tags: + - file + post: + description: Adds one or more tags to an existing asset + operationId: addAssetTags + parameters: + - description: Asset ID + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/json: + schema: + properties: + tags: + description: Tags to add to the asset + items: + type: string + minItems: 1 + type: array + required: + - tags + type: object + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/TagsModificationResponse' + description: Tags added successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset not found + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error (e.g., reserved tag) + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Add tags to asset + tags: + - file + put: + description: Adds and removes tags from an asset in a single operation + operationId: updateAssetTags + parameters: + - description: Asset ID + in: path + name: id + required: true + schema: + format: uuid + type: string + requestBody: + content: + application/json: + schema: + description: At least one of add or remove must contain items. Empty arrays are allowed when the other array has items. + minProperties: 1 + properties: + add: + description: Tags to add to the asset. Can be empty if remove has items. + items: + type: string + type: array + remove: + description: Tags to remove from the asset. Can be empty if add has items. + items: + type: string + type: array + type: object + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/TagsModificationResponse' + description: Tags updated successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Asset not found + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Reserved tag validation error + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Update asset tags + tags: + - file + /api/assets/from-hash: + post: + description: | + Creates a new asset reference using an existing asset's hash. + This avoids re-uploading the file content when the asset already exists in storage. + The user can provide their own metadata and tags for the reference. + operationId: createAssetFromHash + requestBody: + content: + application/json: + schema: + properties: + hash: + description: Hash of the existing asset. Supports Blake3 (blake3:) or SHA256 (sha256:) formats + pattern: ^(blake3|sha256):[a-f0-9]{64}$ + type: string + mime_type: + description: MIME type of the asset (e.g., "image/png", "video/mp4") + type: string + name: + description: Display name for the asset reference (optional) + type: string + tags: + description: Freeform tags for the asset. Common types include "models", "input", "output", and "temp", but any tag can be used in any order. + items: + type: string + minItems: 1 + type: array + user_metadata: + additionalProperties: true + description: Custom metadata for this asset reference + type: object + required: + - hash + - tags + type: object + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/AssetCreated' + description: Asset reference created successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request (bad hash format, invalid tags, etc.) + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Source asset with given hash not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Create asset reference from existing hash + tags: + - file + /api/assets/hash/{hash}: + head: + description: | + Checks if an asset exists in the system by its blake3 hash. + Returns 200 if the asset exists, 404 if it doesn't. + operationId: checkAssetByHash + parameters: + - description: Blake3 hash of the asset in format 'blake3:hex_digest' + in: path + name: hash + required: true + schema: + example: blake3:a1b2c3d4e5f67890123456789012345678901234567890123456789012345678 + pattern: ^blake3:[a-f0-9]{64}$ + type: string + responses: + "200": + description: Asset exists + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid hash format + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + description: Asset not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Check if asset exists by hash + tags: + - file + /api/assets/prune: + post: + description: Starts a background job that removes asset entries whose underlying content no longer exists on disk. + operationId: pruneAssets + responses: + "200": + content: + application/json: + schema: + properties: + marked: + description: Number of assets marked as missing + type: integer + status: + type: string + type: object + description: Prune result + summary: Mark assets whose backing files no longer exist on disk + /api/assets/seed: + post: + description: Starts a background job that scans configured directories and registers assets not yet in the asset database. + operationId: seedAssets + requestBody: + content: + application/json: + schema: + properties: + roots: + description: Root folder paths to scan (if omitted, scans all) + items: + type: string + type: array + type: object + responses: + "200": + content: + application/json: + schema: + properties: + status: + type: string + type: object + description: Seed started + summary: Trigger asset scan/seed from filesystem + /api/assets/seed/cancel: + post: + description: Requests cancellation of the currently-running asset seed job. + operationId: cancelAssetSeed + responses: + "200": + content: + application/json: + schema: + properties: + status: + type: string + type: object + description: Scan cancelled + summary: Cancel an in-progress asset scan + /api/assets/seed/status: + get: + description: Returns progress/status of the most recent asset seed job. + operationId: getAssetSeedStatus + responses: + "200": + content: + application/json: + schema: + additionalProperties: true + description: Scan progress details (files scanned, total, status, etc.) + type: object + description: Scan progress + summary: Get asset scan progress + /api/assets/tags/refine: + get: + description: | + Returns a histogram of tags appearing on assets matching the given filters. + Useful for refining asset searches by showing available tags and their counts. + Only returns tags with non-zero counts (tags that exist on matching assets). + operationId: getAssetTagHistogram + parameters: + - description: Filter assets that have ALL of these tags + explode: false + in: query + name: include_tags + schema: + items: + type: string + type: array + style: form + - description: Exclude assets that have ANY of these tags + explode: false + in: query + name: exclude_tags + schema: + items: + type: string + type: array + style: form + - description: Filter assets where name contains this substring (case-insensitive) + in: query + name: name_contains + schema: + type: string + - description: JSON object for filtering by metadata fields + in: query + name: metadata_filter + schema: + type: string + - description: Maximum number of tags to return (1-1000, default 100) + in: query + name: limit + schema: + default: 100 + maximum: 1000 + minimum: 1 + type: integer + - description: Whether to include public/shared assets in results + in: query + name: include_public + schema: + default: true + type: boolean + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/AssetTagHistogramResponse' + description: Success - Tag histogram returned + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get tag histogram for filtered assets + tags: + - file + /api/embeddings: + get: + description: Returns the list of text-encoder embeddings available on disk. + operationId: getEmbeddings + responses: + "200": + content: + application/json: + schema: + items: + type: string + type: array + description: Embedding names + summary: List available embedding names + /api/experiment/models: + get: + description: | + Returns a list of model folders available in the system. + This is an experimental endpoint that replaces the legacy /models endpoint. + operationId: getModelFolders + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/ModelFolder' + type: array + description: Success - List of model folders + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + security: [] + summary: Get available model folders + tags: + - file + /api/experiment/models/{folder}: + get: + description: | + Returns a list of models available in the specified folder. + This is an experimental endpoint that provides enhanced model information. + operationId: getModelsInFolder + parameters: + - description: The folder name to list models from + in: path + name: folder + required: true + schema: + example: checkpoints + type: string + responses: + "200": + content: + application/json: + schema: + items: + $ref: '#/components/schemas/ModelFile' + type: array + description: Success - List of models in the folder + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Folder not found or no models in folder + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + security: [] + summary: Get models in a specific folder + tags: + - file + /api/extensions: + get: + description: | + Returns the list of custom node web extension JS files available for + loading by the ComfyUI frontend. Paths are relative to the web root + (e.g. `/extensions/VHS.core.js`). + operationId: getExtensions + responses: + "200": + content: + application/json: + schema: + description: URL paths (relative to web root) of available extension JS files + items: + type: string + type: array + description: JSON array of extension file paths + security: [] + summary: List custom node JS extensions + tags: + - node + /api/features: + get: + description: Returns the server's feature capabilities + operationId: getFeatures + responses: + "200": + content: + application/json: + schema: + additionalProperties: true + properties: + max_upload_size: + description: Maximum upload size in bytes + type: integer + supports_preview_metadata: + description: Whether the server supports preview metadata + type: boolean + type: object + description: Success + headers: + Cache-Control: + description: Short-lived private cache to deduplicate rapid-fire calls from the frontend + schema: + type: string + Vary: + description: Cache key includes auth headers so anonymous and authenticated responses are stored separately + schema: + type: string + security: + - ApiKeyAuth: [] + - BearerAuth: [] + - CookieAuth: [] + - {} + summary: Get server feature flags + tags: + - node + /api/feedback: + post: + description: Submit feedback about the ComfyUI service + operationId: submitFeedback + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/FeedbackRequest' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/FeedbackResponse' + description: Feedback submitted successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Submit user feedback + tags: + - feedback + /api/files/mask-layers: + get: + description: | + Given a mask file (any of the 4 layers), returns all related mask layer files. + This is used by the mask editor to load the paint, mask, and painted layers + when reopening a previously edited mask. + operationId: getMaskLayers + parameters: + - description: Hash filename of any mask layer file + in: query + name: filename + required: true + schema: + example: abc123def456.png + type: string + responses: + "200": + content: + application/json: + schema: + properties: + mask: + description: Filename of the mask layer + nullable: true + type: string + paint: + description: Filename of the paint strokes layer + nullable: true + type: string + painted: + description: Filename of the painted image layer + nullable: true + type: string + painted_masked: + description: Filename of the final composite layer + nullable: true + type: string + type: object + description: Success - Related mask layers returned + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: File not found or not a mask file + summary: Get related mask layer files + tags: + - file + /api/free: + post: + description: Frees GPU memory by unloading models and/or freeing the resident model cache. + operationId: freeMemory + requestBody: + content: + application/json: + schema: + properties: + free_memory: + description: Run garbage collection and free cached memory + type: boolean + unload_models: + description: Unload all models from VRAM/RAM + type: boolean + type: object + responses: + "200": + description: Memory freed + summary: Free GPU memory and/or unload models + /api/global_subgraphs: + get: + description: | + Returns a list of globally available subgraph blueprints. + These are pre-built workflow components that can be used as nodes. + The data field contains a promise that resolves to the full subgraph JSON. + operationId: getGlobalSubgraphs + responses: + "200": + content: + application/json: + schema: + additionalProperties: + $ref: '#/components/schemas/GlobalSubgraphInfo' + type: object + description: Success - Map of subgraph IDs to their metadata + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + security: [] + summary: Get available subgraph blueprints + tags: + - workflow + /api/global_subgraphs/{id}: + get: + description: Returns the full data for a specific subgraph blueprint by ID + operationId: getGlobalSubgraph + parameters: + - description: The unique identifier of the subgraph blueprint + in: path + name: id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/GlobalSubgraphData' + description: Success - Full subgraph data + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Subgraph not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + security: [] + summary: Get a specific subgraph blueprint + tags: + - workflow + /api/history: + post: + deprecated: true + description: | + **Deprecated.** Superseded by the job-management endpoints under + `/api/jobs`. Planned for removal no earlier than a future major + release; sunset timeline TBD. - PendingInvite: - type: object - x-runtime: [cloud] - description: '[cloud-only] An outstanding workspace invitation that has not yet been accepted.' - required: - - id - - email - - invited_at - - expires_at - properties: - id: - type: string - description: Invite ID - email: - type: string - format: email - description: Email address of the invited user - token: - type: string - description: Invite token for constructing invite links. Empty for expired invites. - invited_at: - type: string - format: date-time - description: When the invite was created - expires_at: - type: string - format: date-time - description: When the invite expires + Clear all history for the authenticated user or delete specific job IDs. + Supports clearing all history or deleting specific job IDs. + operationId: manageHistory + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/HistoryManageRequest' + required: true + responses: + "200": + description: Success - History management operation completed + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Manage execution history + tags: + - workflow + /api/history_v2: + get: + deprecated: true + description: | + **Deprecated.** Superseded by `GET /api/jobs`, which returns the same + execution records in a paginated, filterable format. Planned for removal + no earlier than a future major release; sunset timeline TBD. - Plan: - type: object - x-runtime: [cloud] - description: '[cloud-only] Billing plan details including pricing, limits, and features.' - required: - - slug - - tier - - duration - - price_cents - - credits_cents - - max_seats - - availability - - seat_summary - properties: - slug: - type: string - description: Plan identifier (e.g., "pro-monthly", "team-standard-annual") - example: pro-monthly - tier: - $ref: '#/components/schemas/SubscriptionTier' - duration: - $ref: '#/components/schemas/SubscriptionDuration' - price_cents: - type: integer - format: int64 - description: Per-member price in cents (base + one seat) - example: 10000 - credits_cents: - type: integer - format: int64 - description: Per-member credits in cents (base + one seat) - example: 10000 - max_seats: - type: integer - format: int64 - description: Maximum number of seats allowed for this plan - example: 20 - availability: - $ref: '#/components/schemas/PlanAvailability' - seat_summary: - $ref: '#/components/schemas/PlanSeatSummary' + Retrieve execution history for the authenticated user with pagination support. + Returns a lightweight history format with filtered prompt data (workflow removed from extra_pnginfo). + operationId: getHistory + parameters: + - description: Maximum number of items to return + in: query + name: max_items + schema: + type: integer + - description: Starting position (default 0) + in: query + name: offset + schema: + default: 0 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/HistoryResponse' + description: Success - Execution history retrieved + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get execution history (v2) + tags: + - workflow + /api/history_v2/{prompt_id}: + get: + deprecated: true + description: | + **Deprecated.** Superseded by `GET /api/jobs/{job_id}`, which returns + the same execution record. Planned for removal no earlier than a future + major release; sunset timeline TBD. - PlanAvailability: - type: object - x-runtime: [cloud] - description: '[cloud-only] Availability and eligibility information for a billing plan.' - required: - - available - properties: - available: - type: boolean - description: Whether the workspace can subscribe to this plan - reason: - $ref: '#/components/schemas/PlanAvailabilityReason' + Retrieve detailed execution history for a specific prompt ID. + Returns full history data including complete prompt information. + operationId: getHistoryForPrompt + parameters: + - description: The prompt ID to retrieve history for + in: path + name: prompt_id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/HistoryDetailResponse' + description: Success - History for prompt retrieved + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Prompt not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get history for specific prompt + tags: + - workflow + /api/i18n: + get: + description: Returns translation file URLs contributed by custom nodes, keyed by locale. + operationId: getI18n + responses: + "200": + content: + application/json: + schema: + additionalProperties: true + description: Nested map of locale to translation key-value pairs + type: object + description: Translation map + summary: Get internationalisation translation strings + /api/interrupt: + post: + description: | + Cancel all currently RUNNING jobs for the authenticated user. + This will interrupt any job that is currently in 'in_progress' status. + Note: This endpoint only affects running jobs. To cancel pending jobs, use /api/queue. + operationId: interruptJob + responses: + "200": + description: Success - Job interrupted or no running job found + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Interrupt currently running jobs + tags: + - queue + /api/job/{job_id}/status: + get: + deprecated: true + description: | + **Deprecated.** Superseded by `GET /api/jobs/{job_id}` (plural path). + Clients should migrate; the endpoint is retained for backward + compatibility but will be removed in a future release. + operationId: getJobStatus + parameters: + - description: The unique ID of the job + in: path + name: job_id + required: true + schema: + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JobStatusResponse' + description: Success - Job status returned + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Forbidden - job belongs to another user + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Job not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get job status (deprecated) + tags: + - job + /api/jobs: + get: + description: | + Retrieve a paginated list of jobs for the authenticated user. + Returns lightweight job data optimized for list views. + Workflow and full outputs are excluded to reduce payload size. + operationId: listJobs + parameters: + - description: Filter by one or more statuses (comma-separated). If not provided, returns all jobs. + example: pending,in_progress + in: query + name: status + schema: + type: string + - description: Filter by workflow ID (exact match) + example: 550e8400-e29b-41d4-a716-446655440000 + in: query + name: workflow_id + schema: + type: string + - description: Filter by output media type (only applies to completed jobs with outputs) + example: image + in: query + name: output_type + schema: + enum: + - image + - video + - audio + - 3d + type: string + - description: Field to sort by (create_time = when job was submitted, execution_time = how long workflow took to run) + example: execution_time + in: query + name: sort_by + schema: + default: create_time + enum: + - create_time + - execution_time + type: string + - description: Sort direction (asc = ascending, desc = descending) + in: query + name: sort_order + schema: + default: desc + enum: + - asc + - desc + type: string + - description: Pagination offset (0-based) + in: query + name: offset + schema: + default: 0 + minimum: 0 + type: integer + - description: Maximum items per page (1-1000) + in: query + name: limit + schema: + default: 100 + maximum: 1000 + minimum: 1 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JobsListResponse' + description: Success - Jobs retrieved + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: List jobs with pagination and filtering + tags: + - workflow + /api/jobs/{job_id}: + get: + description: | + Retrieve complete details for a specific job including workflow and outputs. + Used for detail views, workflow re-execution, and debugging. + operationId: getJobDetail + parameters: + - description: Job identifier (UUID) + in: path + name: job_id + required: true + schema: + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JobDetailResponse' + description: Success - Job details retrieved + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Forbidden - Job does not belong to user + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Job not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get full job details + tags: + - workflow + /api/jobs/{job_id}/cancel: + post: + description: | + Cancel a specific job for the authenticated user. - PlanAvailabilityReason: - type: string - x-runtime: [cloud] - enum: - - same_plan - - incompatible_transition - - requires_team - - requires_personal - - exceeds_max_seats - description: '[cloud-only] Reason why a plan is unavailable' + Idempotent: a job that is already in a terminal state (completed, failed, + cancelled) or already cancelling is treated as a successful no-op and + returns 200. Only truly missing or cross-user jobs return 404. + operationId: cancelJob + parameters: + - description: Job identifier (UUID) + in: path + name: job_id + required: true + schema: + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/JobCancelResponse' + description: Success - Cancel request accepted (or job was already terminal) + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad Request - job_id is not a valid UUID (emitted by request validation before the handler runs) + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Job not found for this user + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error - cancellation failed + summary: Cancel a job + tags: + - workflow + /api/node_replacements: + get: + description: | + Returns mappings of unsupported node class names to their cloud-installed replacements. + Used by the frontend to offer "Quick Fix" when a workflow contains missing nodes. + operationId: getNodeReplacements + responses: + "200": + content: + application/json: + schema: + additionalProperties: true + type: object + description: Success - Node replacement mappings + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + security: [] + summary: Get node replacement mappings + tags: + - node + /api/object_info: + get: + description: Returns information about all available nodes + operationId: getNodeInfo + responses: + "200": + content: + application/json: + schema: + additionalProperties: + $ref: '#/components/schemas/NodeInfo' + type: object + description: Success + summary: Get all node information + tags: + - node + /api/prompt: + get: + description: Returns information about the current prompt in the execution queue + operationId: getPromptInfo + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptInfo' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get information about current prompt execution + tags: + - workflow + post: + description: | + Submit a workflow to be executed by the backend. + The workflow is a JSON object describing the nodes and their connections. + operationId: executePrompt + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PromptRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptResponse' + description: Success - Prompt accepted + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptErrorResponse' + description: Invalid prompt + "402": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptErrorResponse' + description: Payment required - Insufficient credits + "429": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptErrorResponse' + description: Payment required - User has not paid + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptErrorResponse' + description: Internal server error + "503": + content: + application/json: + schema: + $ref: '#/components/schemas/PromptErrorResponse' + description: Service unavailable + summary: Submit a workflow for execution + tags: + - workflow + /api/queue: + get: + description: Returns information about running and pending items in the queue + operationId: getQueueInfo + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/QueueInfo' + description: Success + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + summary: Get queue information + tags: + - queue + post: + description: | + Cancel specific PENDING jobs by ID or clear all pending jobs in the queue. + Note: This endpoint only affects pending jobs. To cancel running jobs, use /api/interrupt. + operationId: manageQueue + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueueManageRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/QueueManageResponse' + description: Success + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Manage queue operations + tags: + - queue + /api/settings: + get: + description: Returns all settings for the authenticated user + operationId: getAllSettings + responses: + "200": + content: + application/json: + schema: + additionalProperties: true + description: User settings as key-value pairs + type: object + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + summary: Get all user settings + tags: + - settings + post: + description: Update multiple settings (merge with existing) + operationId: updateMultipleSettings + requestBody: + content: + application/json: + schema: + additionalProperties: true + description: Settings to update as key-value pairs + type: object + text/plain: + schema: + description: JSON string of settings to update + type: string + required: true + responses: + "200": + content: + application/json: + schema: + additionalProperties: true + description: Updated user settings + type: object + description: Success + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + summary: Update multiple settings + tags: + - settings + /api/settings/{id}: + get: + description: Returns a specific setting value by its id + operationId: getSettingById + parameters: + - description: Setting id to retrieve + in: path + name: id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + description: Setting value response + properties: + value: + description: The setting value + type: object + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Setting not found + summary: Get a specific setting by id + tags: + - settings + post: + description: Update a specific setting by its id + operationId: updateSettingById + parameters: + - description: Setting id to update + in: path + name: id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + description: New value for the setting + text/plain: + schema: + description: JSON string of the new setting value + type: string + required: true + responses: + "200": + content: + application/json: + schema: + description: Updated setting value response + properties: + value: + description: The updated setting value + type: object + description: Success + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + summary: Update a specific setting by id + tags: + - settings + /api/system_stats: + get: + description: Returns system statistics including ComfyUI version, device info, and system resources + operationId: getSystemStats + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/SystemStatsResponse' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + security: [] + summary: Get system statistics + tags: + - system + /api/tags: + get: + description: | + Retrieves a list of all tags used across assets. + Includes usage counts and filtering options. + operationId: listTags + parameters: + - description: Filter tags by prefix + in: query + name: prefix + schema: + type: string + - description: Maximum number of tags to return (1-1000) + in: query + name: limit + schema: + default: 100 + maximum: 1000 + minimum: 1 + type: integer + - description: Number of tags to skip for pagination + in: query + name: offset + schema: + default: 0 + minimum: 0 + type: integer + - description: Sort order for tags + in: query + name: order + schema: + default: count_desc + enum: + - count_desc + - name_asc + type: string + - description: Include tags with zero usage count + in: query + name: include_zero + schema: + default: false + type: boolean + - description: Whether to include public/shared assets when counting tags + in: query + name: include_public + schema: + default: true + type: boolean + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ListTagsResponse' + description: Tags retrieved successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: List all tags + tags: + - file + /api/tasks: + get: + description: | + Retrieve a paginated list of background tasks for the authenticated user. + Supports filtering by task type, status, and creation time. + operationId: listTasks + parameters: + - description: Filter by task type name (exact match) + example: model_upload + in: query + name: task_name + schema: + type: string + - description: Filter by idempotency key (exact match). For best performance, specify task_name as well. + example: upload-model-abc123 + in: query + name: idempotency_key + schema: + type: string + - description: Filter by one or more statuses (comma-separated) + example: created,running + in: query + name: status + schema: + type: string + - description: Filter tasks created after this timestamp (RFC3339 format) + example: "2024-01-01T00:00:00Z" + in: query + name: created_after + schema: + format: date-time + type: string + - description: Filter tasks created before this timestamp (RFC3339 format) + example: "2024-12-31T23:59:59Z" + in: query + name: created_before + schema: + format: date-time + type: string + - description: Sort direction (asc = ascending, desc = descending by create_time) + in: query + name: sort_order + schema: + default: desc + enum: + - asc + - desc + type: string + - description: Pagination offset (0-based) + in: query + name: offset + schema: + default: 0 + minimum: 0 + type: integer + - description: Maximum items per page (1-100) + in: query + name: limit + schema: + default: 20 + maximum: 100 + minimum: 1 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/TasksListResponse' + description: Success - Tasks retrieved + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error - Invalid filter values + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: List background tasks + tags: + - task + /api/tasks/{task_id}: + get: + description: | + Retrieve full details for a specific background task. + operationId: getTask + parameters: + - description: Task identifier (UUID) + in: path + name: task_id + required: true + schema: + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/TaskResponse' + description: Success - Task details retrieved + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized - Authentication required + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Task not found (also returned for ownership failures to avoid leaking task existence) + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get task details + tags: + - task + /api/upload/image: + post: + description: | + Upload an image file to cloud storage. - PlanSeatSummary: - type: object - x-runtime: [cloud] - description: '[cloud-only] Summary of seat costs based on current workspace members' - required: - - seat_count - - total_cost_cents - - total_credits_cents - properties: - seat_count: - type: integer - description: Total number of seats (owner + members) that would be charged - example: 5 - total_cost_cents: - type: integer - format: int64 - description: Total cost for all seats in cents - example: 50000 - total_credits_cents: - type: integer - format: int64 - description: Total credits granted for all seats in cents - example: 50000 + Image limits: + - Maximum file size: 50 MB + - Maximum width/height per edge: 16384 px + - Maximum total pixel count: 64 megapixels (67108864 pixels) - PreviewPlanInfo: - type: object - x-runtime: [cloud] - description: '[cloud-only] Plan information for preview display' - required: - - slug - - tier - - duration - - price_cents - - credits_cents - - seat_summary - properties: - slug: - type: string - description: Plan slug - example: team-pro-monthly - tier: - $ref: '#/components/schemas/SubscriptionTier' - duration: - $ref: '#/components/schemas/SubscriptionDuration' - price_cents: - type: integer - format: int64 - description: Per-seat price in cents - example: 10000 - credits_cents: - type: integer - format: int64 - description: Per-seat credits in cents - example: 10000 - seat_summary: - $ref: '#/components/schemas/PlanSeatSummary' - period_start: - type: string - format: date-time - description: Current billing period start (only for current_plan) - period_end: - type: string - format: date-time - description: Current billing period end (only for current_plan) + Uploads that exceed any of these limits are rejected with HTTP 400. + operationId: uploadImage + requestBody: + content: + multipart/form-data: + schema: + properties: + image: + description: The image file to upload + format: binary + type: string + overwrite: + description: Whether to overwrite existing file (true/false) + type: string + subfolder: + description: Optional subfolder path + type: string + type: + description: Upload type (defaults to "output") + type: string + required: + - image + type: object + required: true + responses: + "200": + content: + application/json: + schema: + properties: + name: + description: Filename of the uploaded image + type: string + subfolder: + description: Subfolder path where image was saved + type: string + type: + description: Type of upload (e.g., "output") + type: string + type: object + description: Image uploaded successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Upload an image file + tags: + - file + /api/upload/mask: + post: + description: | + Upload a mask image to be applied to an existing image. - PreviewSubscribeResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Itemized cost preview for a pending subscription change.' - required: - - allowed - - transition_type - - effective_at - - is_immediate - - cost_today_cents - - cost_next_period_cents - - credits_today_cents - - credits_next_period_cents - - new_plan - properties: - allowed: - type: boolean - description: Whether this subscription change is allowed - reason: - type: string - description: Reason why the change is not allowed (only present if allowed=false) - transition_type: - type: string - enum: - - new_subscription - - upgrade - - downgrade - - duration_change - description: Type of subscription transition - effective_at: - type: string - format: date-time - description: When the change takes effect - is_immediate: - type: boolean - description: Whether the change takes effect immediately (true) or at period end (false) - cost_today_cents: - type: integer - format: int64 - description: Amount to charge today in cents (0 for downgrades) - example: 5000 - cost_next_period_cents: - type: integer - format: int64 - description: Amount that will be charged at next billing period in cents - example: 10000 - credits_today_cents: - type: integer - format: int64 - description: Credits granted today in cents (prorated for mid-period upgrades) - example: 5000 - credits_next_period_cents: - type: integer - format: int64 - description: Credits that will be granted at next billing period in cents - example: 10000 - current_plan: - $ref: '#/components/schemas/PreviewPlanInfo' - new_plan: - $ref: '#/components/schemas/PreviewPlanInfo' + Image limits apply to both the uploaded mask and the referenced + original image: + - Maximum file size: 50 MB + - Maximum width/height per edge: 16384 px + - Maximum total pixel count: 64 megapixels (67108864 pixels) - PublishedWorkflowDetail: - type: object - x-runtime: [cloud] - description: '[cloud-only] Full detail of a publicly published workflow on the Hub.' - required: - - share_id - - workflow_id - - name - - listed - - workflow_json - - assets - properties: - share_id: - type: string - workflow_id: - type: string - name: - type: string - description: Human-readable workflow name. - listed: - type: boolean - publish_time: - type: string - format: date-time - nullable: true - workflow_json: - type: object - additionalProperties: true - description: The workflow JSON content at publish time. - assets: - type: array - description: Published assets with their library status for the caller. - items: - $ref: '#/components/schemas/AssetInfo' - - SecretResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] User secret metadata (the secret value itself is never returned after creation).' - required: - - id - - name - - created_at - - updated_at - properties: - id: - type: string - format: uuid - description: Unique identifier for the secret - name: - type: string - description: User-provided label for the secret - provider: - type: string - description: Provider identifier (e.g., huggingface, civitai) - last_used_at: - type: string - format: date-time - description: When the secret was last used for decryption - created_at: - type: string - format: date-time - description: When the secret was created - updated_at: - type: string - format: date-time - description: When the secret was last updated - - SubscriptionDuration: - type: string - x-runtime: [cloud] - enum: - - MONTHLY - - ANNUAL - description: '[cloud-only] Billing period (uppercase to match comfy-api)' - - SubscriptionTier: - type: string - x-runtime: [cloud] - enum: - - FREE - - STANDARD - - CREATOR - - PRO - - FOUNDERS_EDITION - description: '[cloud-only] Subscription tier (uppercase to match comfy-api)' - - UserDataResponseFull: - type: object - x-runtime: [cloud] - description: '[cloud-only] User data listing entry with file metadata (path, size, modification time).' - properties: - path: - type: string - size: - type: integer - modified: - type: integer - format: int64 - description: UNIX timestamp of the last modification in milliseconds. - - ValidationError: - type: object - x-runtime: [cloud] - description: '[cloud-only] Details of a single validation error encountered during asset operations.' - required: - - code - - message - - field - properties: - code: - type: string - description: Machine-readable error code - example: FORMAT_NOT_ALLOWED - message: - type: string - description: Human-readable error message - example: 'File format "PickleTensor" is not allowed. Allowed formats: [SafeTensor]' - field: - type: string - description: Field that failed validation - example: format - - ValidationResult: - type: object - x-runtime: [cloud] - description: '[cloud-only] Result of validating a set of asset operations.' - required: - - is_valid - properties: - is_valid: - type: boolean - description: Overall validation status (true if all checks passed) - example: true - errors: - type: array - items: - $ref: '#/components/schemas/ValidationError' - description: Blocking validation errors that prevent download - warnings: - type: array - items: - $ref: '#/components/schemas/ValidationError' - description: Non-blocking validation warnings (informational only) - - WorkflowForkedFrom: - type: object - x-runtime: [cloud] - description: '[cloud-only] Reference to the parent workflow from which this workflow was forked.' - properties: - workflow_id: - type: string - workflow_version_id: - type: string - - WorkflowResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Full workflow entity including metadata and version history.' - required: - - id - - latest_version - - created_by - - created_at - - updated_at - properties: - id: - type: string - name: - type: string - description: - type: string - default_view: - type: string - enum: - - workflow - - app - latest_version: - type: integer - forked_from: - $ref: '#/components/schemas/WorkflowForkedFrom' - created_by: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - WorkflowVersionContentResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Full workflow version including the serialized workflow JSON.' - required: - - id - - version - - workflow_json - - created_by - - created_at - properties: - id: - type: string - version: - type: integer - workflow_json: - type: object - additionalProperties: true - created_by: - type: string - created_at: - type: string - format: date-time - dependency_asset_ids: - type: array - items: - type: string - - WorkspaceAPIKeyInfo: - type: object - x-runtime: [cloud] - description: '[cloud-only] Metadata for a workspace-scoped API key (secret is never returned).' - required: - - id - - workspace_id - - user_id - - name - - description - - key_prefix - - created_at - properties: - id: - type: string - format: uuid - description: API key ID - workspace_id: - type: string - description: Workspace this key belongs to - user_id: - type: string - description: User who created this key - name: - type: string - description: User-provided label - description: - type: string - description: User-provided description of the key's purpose. Limit is byte-based (UTF-8 encoding); 5000 bytes equals - 5000 ASCII characters or fewer multi-byte characters. - maxLength: 5000 - key_prefix: - type: string - description: First 8 chars after prefix for display - expires_at: - type: string - format: date-time - description: When the key expires (if set) - last_used_at: - type: string - format: date-time - description: Last time the key was used - revoked_at: - type: string - format: date-time - description: When the key was revoked (if revoked) - created_at: - type: string - format: date-time - description: When the key was created - - WorkspaceSummary: - type: object - x-runtime: [cloud] - description: '[cloud-only] Abbreviated workspace metadata used in list responses.' - required: - - id - - name - - type - properties: - id: - type: string - example: w-a1b2c3d4-5678-90ab-cdef-1234567890ab - name: - type: string - example: My Team - type: - type: string - enum: - - personal - - team - - WorkspaceWithRole: - type: object - x-runtime: [cloud] - description: '[cloud-only] Workspace entity annotated with the requesting user''s role.' - required: - - id - - name - - type - - role - - created_at - - joined_at - properties: - id: - type: string - example: w-a1b2c3d4-5678-90ab-cdef-1234567890ab - name: - type: string - example: My Team - type: - type: string - enum: - - personal - - team - role: - type: string - enum: - - owner - - member - created_at: - type: string - format: date-time - description: When the workspace was created - joined_at: - type: string - format: date-time - description: When the user joined the workspace (same as created_at for the workspace creator) - subscription_tier: - $ref: '#/components/schemas/SubscriptionTier' - - BindingErrorResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Error shape returned when request binding or validation fails before the handler runs.' - required: - - message - properties: - message: - type: string - - ErrorResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Standard error response from cloud endpoints with a machine-readable code and human-readable message.' - required: - - code - - message - properties: - code: - type: string - description: Machine-readable error code - message: - type: string - description: Human-readable error message - - AcceptInviteResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response returned after successfully accepting a workspace invitation.' - required: - - workspace_id - - workspace_name - properties: - workspace_id: - type: string - description: ID of the workspace joined - workspace_name: - type: string - description: Name of the workspace joined - - BillingEventsResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Paginated list of billing events for a workspace.' - required: - - total - - events - - page - - limit - - totalPages - properties: - total: - type: integer - description: Total number of events - events: - type: array - items: - $ref: '#/components/schemas/BillingEvent' - page: - type: integer - description: Current page number (1-indexed) - limit: - type: integer - description: Items per page - totalPages: - type: integer - description: Total number of pages - - BillingOpStatusResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Status of an asynchronous billing operation.' - required: - - id - - status - - started_at - properties: - id: - type: string - description: Unique identifier for the billing operation - status: - type: string - enum: - - pending - - succeeded - - failed - description: Current status of the operation - error_message: - type: string - description: Error message if status is failed - started_at: - type: string - format: date-time - description: When the operation was initiated - completed_at: - type: string - format: date-time - description: When the operation completed (success or failure) - - CancelSubscriptionResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response after successfully cancelling a subscription.' - required: - - cancel_at - - billing_op_id - properties: - billing_op_id: - type: string - description: Billing operation ID to poll for status via GET /api/billing/ops/{id} - cancel_at: - type: string - format: date-time - description: The date when the subscription will end (end of current billing period) - - CreateTopupResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response after successfully purchasing a credit top-up.' - required: - - topup_id - - status - - amount_cents - - billing_op_id - properties: - billing_op_id: - type: string - description: Billing operation ID to poll for status via GET /api/billing/ops/{id} - topup_id: - type: string - description: Unique identifier for the top-up request (same as billing_op_id, deprecated) - status: - type: string - enum: - - pending - - completed - - failed - description: Current status of the top-up - amount_cents: - type: integer - format: int64 - description: Amount being charged in cents - - CreateWorkspaceAPIKeyResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response containing the newly created workspace API key.' - required: - - id - - name - - description - - key - - key_prefix - - created_at - properties: - id: - type: string - format: uuid - description: API key ID - name: - type: string - description: User-provided label - description: - type: string - description: User-provided description of the key's purpose. Limit is byte-based (UTF-8 encoding); 5000 bytes equals - 5000 ASCII characters or fewer multi-byte characters. - maxLength: 5000 - key: - type: string - description: The full plaintext API key (only shown once) - key_prefix: - type: string - description: First 8 chars after prefix for display - expires_at: - type: string - format: date-time - description: When the key expires (if set) - created_at: - type: string - format: date-time - description: When the key was created - - ExchangeTokenResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response containing the issued Cloud JWT and its expiry.' - required: - - token - - expires_at - - workspace - - role - - permissions - properties: - token: - type: string - description: Cloud JWT token - expires_at: - type: string - format: date-time - description: Token expiration time (RFC 3339) - workspace: - $ref: '#/components/schemas/WorkspaceSummary' - role: - type: string - enum: - - owner - - member - description: User's role in the workspace - permissions: - type: array - items: - type: string - description: Permission strings for the role - example: - - owner:* - - JobCancelResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response for POST /api/jobs/{job_id}/cancel. Returned on both fresh cancels and idempotent no-ops.' - required: - - cancelled - properties: - cancelled: - type: boolean - description: "True when a cancel event was successfully dispatched by this call.\nFalse when the job was already in\ - \ a terminal or cancelling state,\nin which case the call is a no-op (still 200 \u2014 idempotent).\n" - - ResubscribeResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response after successfully resubscribing to a billing plan.' - required: - - status - - billing_op_id - properties: - billing_op_id: - type: string - description: Billing operation ID to poll for status via GET /api/billing/ops/{id} - status: - type: string - enum: - - active - description: The subscription status after resubscribing - message: - type: string - description: Human-readable confirmation message - - SubscribeResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Response after successfully subscribing to a billing plan.' - required: - - status - - billing_op_id - properties: - billing_op_id: - type: string - description: Billing operation ID to poll for status via GET /api/billing/ops/{id} - status: - type: string - enum: - - subscribed - - needs_payment_method - - pending_payment - description: 'Status of the subscription operation: - - - subscribed: Subscription is active immediately - - - needs_payment_method: User must add payment method via payment_method_url - - - pending_payment: Upgrade initiated, waiting for payment to complete - - ' - effective_at: - type: string - format: date-time - description: When the subscription became/becomes active (present when status=subscribed or pending_payment) - payment_method_url: - type: string - description: URL to redirect user to add payment method (present when status=needs_payment_method) - - UserResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] User information response' - required: - - id - - status - properties: - id: - type: string - description: Firebase UID of the authenticated user - status: - type: string - description: User status (always "active" for authenticated users) - - WorkflowListResponse: - type: object - x-runtime: [cloud] - description: '[cloud-only] Paginated list of saved workflows.' - required: - - data - - pagination - properties: - data: - type: array - items: - $ref: '#/components/schemas/WorkflowResponse' - pagination: - $ref: '#/components/schemas/PaginationInfo' - - FeedbackRequest: - type: object - x-runtime: [cloud] - description: "[cloud-only] User feedback submission body." - required: - - message - properties: - type: - type: string - enum: - - missing_nodes - - general - - missing_models - description: Feedback category - category: - type: string - description: Additional category metadata - message: - type: string - description: User-provided feedback message + Uploads that exceed any of these limits are rejected with HTTP 400. + operationId: uploadMask + requestBody: + content: + multipart/form-data: + schema: + properties: + image: + description: The mask image file to upload + format: binary + type: string + original_ref: + description: JSON string containing reference to the original image + type: string + required: + - image + - original_ref + type: object + required: true + responses: + "200": + content: + application/json: + schema: + properties: + name: + description: Filename of the uploaded mask + type: string + subfolder: + description: Subfolder path where mask was saved + type: string + type: + description: Type of upload (e.g., "output") + type: string + type: object + description: Mask uploaded successfully + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Upload a mask image + tags: + - file + /api/user: + get: + description: Returns information about the currently authenticated user + operationId: getUser + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/UserResponse' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + summary: Get current user information + tags: + - user + /api/userdata: + get: + description: Returns a list of user data files in the specified directory, optionally recursively and with full metadata. + operationId: getUserdata + parameters: + - description: The directory to list files from. + in: query + name: dir + schema: + type: string + - description: Whether to list files recursively. + in: query + name: recurse + schema: + default: false + type: boolean + - description: Whether to split file information by type. + in: query + name: split + schema: + default: false + type: boolean + - description: Whether to return full file metadata. + in: query + name: full_info + schema: + default: false + type: boolean + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/GetUserDataResponseFull' + description: A list of user data files. + "400": + content: + text/plain: + schema: + type: string + description: Bad request (e.g., invalid filename). + "401": + content: + text/plain: + schema: + type: string + description: Unauthorized. + "404": + content: + text/plain: + schema: + type: string + description: File not found or invalid path. + "500": + content: + text/plain: + schema: + type: string + description: General error + summary: List user data files + tags: + - user + /api/userdata/{file}: + delete: + description: | + Delete a user data file from the database. The file parameter should be + the relative path within the user's data directory. + operationId: deleteUserdataFile + parameters: + - description: The file path to delete (URL encoded if necessary). + in: path + name: file + required: true + schema: + type: string + responses: + "204": + description: File deleted successfully (No Content). + "401": + content: + text/plain: + schema: + type: string + description: Unauthorized. + "404": + content: + text/plain: + schema: + type: string + description: File not found. + "500": + content: + text/plain: + schema: + type: string + description: Internal server error. + summary: Delete a user data file + tags: + - user + get: + description: Returns the requested user data file if it exists. + operationId: getUserdataFile + parameters: + - description: The filename of the user data to retrieve. + in: path + name: file + required: true + schema: + type: string + responses: + "200": + content: + application/octet-stream: + schema: + format: binary + type: string + description: Successfully retrieved the file. + "400": + content: + text/plain: + schema: + type: string + description: Bad request (e.g., invalid filename). + "401": + content: + text/plain: + schema: + type: string + description: Unauthorized. + "404": + content: + text/plain: + schema: + type: string + description: File not found or invalid path. + "500": + content: + text/plain: + schema: + type: string + description: General error + summary: Get user data file + tags: + - user + post: + description: | + Upload a file to a user's data directory. Optional query parameters allow + control over overwrite behavior and response detail. + operationId: postUserdataFile + parameters: + - description: The target file path (URL encoded if necessary). + in: path + name: file + required: true + schema: + type: string + - description: If "false", prevents overwriting existing files. Defaults to "true". + in: query + name: overwrite + schema: + default: "true" + enum: + - "true" + - "false" + type: string + - description: If "true", returns detailed file info; if "false", returns only the relative path. + in: query + name: full_info + schema: + default: "false" + enum: + - "true" + - "false" + type: string + requestBody: + content: + application/octet-stream: + schema: + format: binary + type: string + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/UserDataResponseFull' + description: File uploaded successfully. + "400": + content: + text/plain: + schema: + type: string + description: Missing or invalid 'file' parameter. + "401": + content: + text/plain: + schema: + type: string + description: Unauthorized. + "403": + content: + text/plain: + schema: + type: string + description: The requested path is not allowed. + "409": + content: + text/plain: + schema: + type: string + description: File already exists and overwrite is set to false. + "500": + content: + text/plain: + schema: + type: string + description: General error + summary: Upload or update a user data file + tags: + - user + /api/userdata/{file}/move/{dest}: + post: + description: | + Move or rename a file within a user's data directory, with options for + controlling overwrite behavior and response format. + operationId: moveUserdataFile + parameters: + - description: The source file path (URL encoded if necessary). + in: path + name: file + required: true + schema: + type: string + - description: The destination file path (URL encoded if necessary). + in: path + name: dest + required: true + schema: + type: string + - description: If "false", prevents overwriting existing files. Defaults to "true". + in: query + name: overwrite + schema: + default: "true" + enum: + - "true" + - "false" + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/UserDataResponseFull' + description: File moved successfully. + "400": + content: + text/plain: + schema: + type: string + description: Missing or invalid parameters. + "401": + content: + text/plain: + schema: + type: string + description: Unauthorized. + "404": + content: + text/plain: + schema: + type: string + description: Source file not found. + "409": + content: + text/plain: + schema: + type: string + description: Destination file already exists and overwrite is set to false. + "500": + content: + text/plain: + schema: + type: string + description: General error + summary: Move or rename a user data file + tags: + - user + /api/userdata/{file}/publish: + get: + description: Returns the publish status and share info for a workflow identified by its userdata path. + operationId: getUserdataFilePublish + parameters: + - description: The workflow file path within the user's data directory (URL encoded if necessary). + in: path + name: file + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowPublishInfo' + description: Publish info (publish_time is null if never published) + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get publish info for a workflow file + tags: + - workflows + post: + description: Creates a new published_workflow record from the latest version and snapshots the provided assets. + operationId: postUserdataFilePublish + parameters: + - description: The workflow file path within the user's data directory (URL encoded if necessary). + in: path + name: file + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PublishWorkflowAssetsRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowPublishInfo' + description: Workflow published + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Publish a workflow file + tags: + - workflows + /api/users: + get: + description: | + ComfyUI legacy users endpoint. Returns information about how user + data is stored. In cloud this is always server-managed, so callers + receive a constant response indicating server-side storage. + operationId: getUsersInfo + responses: + "200": + content: + application/json: + schema: + properties: + migrated: + description: Whether user data has been migrated (always true in cloud) + type: boolean + storage: + description: Where user data is stored (always "server" in cloud) + type: string + required: + - storage + - migrated + type: object + description: Userdata storage information + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + summary: ComfyUI userdata storage info + tags: + - user + /api/vhs/queryvideo: + get: + description: | + VHS custom node endpoint that returns metadata about a video file + (frame count, fps, resolution, duration). Currently returns default + placeholder values; real ffprobe integration is a follow-up. + operationId: getVhsQueryVideo + parameters: + - description: Name of the video file to query + in: query + name: filename + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + properties: + source: + description: Source video metadata + properties: + duration: + description: Duration in seconds + type: number + fps: + description: Frames per second + type: number + frames: + description: Total frame count + type: integer + size: + description: '[width, height] in pixels' + items: + type: integer + maxItems: 2 + minItems: 2 + type: array + required: + - size + - fps + - frames + - duration + type: object + required: + - source + type: object + description: Video metadata + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: | + Missing required query parameter. Produced by the oapi-codegen + wrapper via echo.NewHTTPError; the custom Echo HTTPErrorHandler + normalizes it to the standard ErrorResponse {code, message} shape + (BE-1178). + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + security: + - ApiKeyAuth: [] + - BearerAuth: [] + - CookieAuth: [] + summary: Query VHS video metadata + tags: + - file + /api/view: + get: + description: | + Retrieve and view a file from the ComfyUI file system. + This endpoint is typically used to view generated images or other output files. + Cookie auth is allowed on this endpoint because it's used by img/video tags in browsers. + operationId: viewFile + parameters: + - description: Name of the file to view + in: query + name: filename + required: true + schema: + example: ComfyUI_00004_.png + type: string + - description: Subfolder path where the file is located + in: query + name: subfolder + schema: + example: tests/foo/bar + type: string + - description: Type of file (e.g., output, input, temp) + in: query + name: type + schema: + example: output + type: string + - description: Full path to the file (used for temp files) + in: query + name: fullpath + schema: + type: string + - description: Format of the file + in: query + name: format + schema: + type: string + - description: Frame rate for video files + in: query + name: frame_rate + schema: + type: integer + - description: Workflow identifier + in: query + name: workflow + schema: + type: string + - description: Timestamp parameter + in: query + name: timestamp + schema: + example: 1234567890 + type: integer + - description: | + Image channel to extract from PNG images. + - 'rgb': Return only RGB channels (alpha set to fully opaque) + - 'a' or 'alpha': Return alpha channel as grayscale image + - If not specified, return original image unchanged via redirect + in: query + name: channel + schema: + example: rgb + type: string + - description: | + Maximum dimension (width or height) to resize the image to, preserving aspect ratio. + The image is fit within a res x res box. Returns a JPEG thumbnail. + Only applies to raster image files (PNG, JPEG, WebP, GIF). + in: query + name: res + schema: + example: 256 + maximum: 1024 + minimum: 64 + type: integer + responses: + "200": + content: + image/jpeg: + schema: + description: Resized JPEG thumbnail (returned when res parameter is used) + format: binary + type: string + image/png: + schema: + description: Processed PNG image with extracted channel + format: binary + type: string + description: Success - File content returned (used when channel or res parameter is present) + "302": + description: Redirect to GCS signed URL + headers: + Cache-Control: + description: Cache directive for the redirect response + schema: + type: string + Location: + description: Signed URL to access the file in GCS + schema: + type: string + Vary: + description: Headers that affect response caching + schema: + type: string + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Invalid request parameters + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: File not found or unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + security: + - ApiKeyAuth: [] + - BearerAuth: [] + - CookieAuth: [] + summary: View a file + tags: + - file + /api/workflow_templates: + get: + description: Returns available workflow templates + operationId: getWorkflowTemplates + responses: + "200": + content: + application/json: + schema: + description: Empty object for workflow templates + type: object + description: Success + security: [] + summary: Get available workflow templates + tags: + - workflow + /api/workflows: + get: + description: Returns a paginated list of workflows for the authenticated user in the current workspace. + operationId: listWorkflows + parameters: + - in: query + name: limit + schema: + default: 20 + maximum: 100 + type: integer + - in: query + name: offset + schema: + default: 0 + type: integer + - description: Search workflows by name (case-insensitive substring match) + in: query + name: name + schema: + type: string + - description: Filter by default view type + in: query + name: default_view + schema: + enum: + - workflow + - app + type: string + - description: Sort field + in: query + name: sort + schema: + default: create_time + enum: + - create_time + - update_time + - name + type: string + - description: Sort order + in: query + name: order + schema: + default: desc + enum: + - asc + - desc + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowListResponse' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: List workflows + tags: + - workflows + post: + description: Creates a new workflow with its first version. + operationId: createWorkflow + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWorkflowRequest' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowResponse' + description: Workflow created successfully + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Create a new workflow + tags: + - workflows + /api/workflows/{workflow_id}: + delete: + description: Soft-deletes a workflow. + operationId: deleteWorkflow + parameters: + - description: The UUID of the workflow to delete. + in: path + name: workflow_id + required: true + schema: + type: string + responses: + "204": + description: Workflow deleted successfully + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Delete workflow + tags: + - workflows + get: + description: Retrieves workflow metadata by ID. + operationId: getWorkflow + parameters: + - description: The UUID of the workflow. + in: path + name: workflow_id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowResponse' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get workflow + tags: + - workflows + patch: + description: Updates mutable workflow metadata (name, description, default_view). + operationId: updateWorkflow + parameters: + - description: The UUID of the workflow to update. + in: path + name: workflow_id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWorkflowRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowResponse' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Update workflow metadata + tags: + - workflows + /api/workflows/{workflow_id}/content: + get: + description: Retrieves the latest version of a workflow and its JSON content. + operationId: getWorkflowContent + parameters: + - description: The UUID of the workflow whose content should be retrieved. + in: path + name: workflow_id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowVersionContentResponse' + description: Success + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get workflow content + tags: + - workflows + /api/workflows/{workflow_id}/fork: + post: + description: Creates a new workflow by forking from an existing version. + operationId: forkWorkflow + parameters: + - description: The UUID of the source workflow to fork from. + in: path + name: workflow_id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ForkWorkflowRequest' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowResponse' + description: Workflow forked successfully + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Forbidden + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Source workflow or version not found + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Fork a workflow + tags: + - workflows + /api/workflows/{workflow_id}/versions: + post: + description: Creates a new workflow version with updated workflow JSON. Uses optimistic concurrency via base_version. + operationId: createWorkflowVersion + parameters: + - description: The UUID of the workflow to create a new version for. + in: path + name: workflow_id + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWorkflowVersionRequest' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowVersionResponse' + description: Version created successfully + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Forbidden - not the workflow owner + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow not found + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Version conflict - base_version does not match latest + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Validation error + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Create a new version + tags: + - workflows + /api/workflows/published/{share_id}: + get: + description: | + Returns the published workflow details including the status of each + published asset relative to the caller's library. Authentication is required. + operationId: getPublishedWorkflow + parameters: + - description: The share ID of the published workflow. + in: path + name: share_id + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/PublishedWorkflowDetail' + description: Published workflow details with asset statuses + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Unauthorized + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Share not found + "413": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Workflow JSON too large + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + summary: Get a published workflow by share ID + tags: + - workflows + /health: + get: + description: | + Returns `200 OK` if the database is reachable and dynamic config has + loaded, otherwise `503 Service Unavailable`. Used by the GKE ingress + for health checks. Response body is plain text for probe simplicity. + operationId: getHealth + responses: + "200": + content: + text/plain: + schema: + example: OK + type: string + description: Service is healthy + "503": + content: + text/plain: + schema: + example: Service Unavailable + type: string + description: Service is unhealthy + security: [] + summary: Health probe for Kubernetes readiness/liveness + tags: + - system + /internal/folder_paths: + get: + description: Returns the filesystem paths ComfyUI loads models and assets from, keyed by folder type. + operationId: getInternalFolderPaths + responses: + "200": + content: + application/json: + schema: + additionalProperties: + items: + items: + type: string + type: array + type: array + description: Map of folder type name to list of path entries + type: object + description: Dictionary of folder type to paths + summary: Get configured folder paths + /internal/logs: + get: + description: Returns ComfyUI log entries from the in-memory log buffer. + operationId: getInternalLogs + responses: + "200": + content: + text/plain: + schema: + type: string + description: Log text + summary: Get server logs as text + /internal/logs/raw: + get: + description: Returns the raw ComfyUI log buffer plus size metadata. + operationId: getInternalLogsRaw + responses: + "200": + content: + application/json: + schema: + properties: + entries: + items: + properties: + m: + description: Message + type: string + t: + description: Timestamp + type: number + type: object + type: array + size: + properties: + cols: + type: integer + rows: + type: integer + type: object + type: object + description: Structured log data + summary: Get raw structured log entries + /internal/logs/subscribe: + patch: + description: Subscribes or unsubscribes the current client from live log streaming over the WebSocket. + operationId: subscribeToLogs + requestBody: + content: + application/json: + schema: + properties: + clientId: + description: WebSocket client ID + type: string + enabled: + description: Enable or disable log streaming for this client + type: boolean + required: + - clientId + - enabled + type: object + required: true + responses: + "200": + description: Subscription updated + summary: Subscribe or unsubscribe a WebSocket client to log streaming +security: + - ApiKeyAuth: [] + - BearerAuth: [] +servers: + - description: Default ComfyUI server + url: / +tags: + - description: Workflow execution and management + name: workflow + - description: Node information + name: node + - description: File operations + name: file + - description: User settings management + name: settings + - description: User feedback management + name: feedback + - description: System operations and monitoring + name: system + - description: User information and management + name: user + - description: Background task management + name: task + - description: Workflow storage and version management + name: workflows + - description: Job queue state and control + name: queue + - description: Job lifecycle queries + name: job From 7758b9b321ae65237f05369c43d7b0b493eabb54 Mon Sep 17 00:00:00 2001 From: "Yousef R. Gamaleldin" <81116377+yousef-rafat@users.noreply.github.com> Date: Thu, 4 Jun 2026 02:03:32 +0300 Subject: [PATCH 12/21] fix: Image grid bug fix (CORE-215) (#14100) --- comfy_extras/nodes_dataset.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/comfy_extras/nodes_dataset.py b/comfy_extras/nodes_dataset.py index 104d16d91..0253b4b4f 100644 --- a/comfy_extras/nodes_dataset.py +++ b/comfy_extras/nodes_dataset.py @@ -411,6 +411,21 @@ class ImageProcessingNode(io.ComfyNode): return has_group + @classmethod + def _ensure_image_list(cls, images): + """Normalize to a flat list of [1, H, W, C] tensors.""" + if isinstance(images, torch.Tensor): + if images.ndim != 4: + raise ValueError(f"Expected 4D image tensor, got shape {tuple(images.shape)}") + return [images[i:i+1] for i in range(images.shape[0])] + + flat = [] + for item in images: + if not isinstance(item, torch.Tensor) or item.ndim != 4: + raise ValueError(f"Expected 4D image tensor, got {type(item).__name__} shape {getattr(item, 'shape', None)}") + flat.extend([item[i:i+1] for i in range(item.shape[0])]) + return flat + @classmethod def define_schema(cls): if cls.node_id is None: @@ -458,6 +473,9 @@ class ImageProcessingNode(io.ComfyNode): """Execute the node. Routes to _process or _group_process based on mode.""" is_group = cls._detect_processing_mode() + if is_group: + images = cls._ensure_image_list(images) + # Extract scalar values from lists for parameters params = {} for k, v in kwargs.items(): From 4f99ce0f8c77e8de7318da1b70d745e55ece6022 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Thu, 4 Jun 2026 02:05:48 +0300 Subject: [PATCH 13/21] [Partner Nodes] fix SaveWEBM node to save alpha channel; add BriaTransparentVideoBackground Partner node (#14257) --- comfy_api_nodes/nodes_bria.py | 96 ++++++++++++++++++++++++++++++++++- comfy_extras/nodes_video.py | 13 +++-- 2 files changed, 105 insertions(+), 4 deletions(-) diff --git a/comfy_api_nodes/nodes_bria.py b/comfy_api_nodes/nodes_bria.py index 69b0233af..ce2c9e9be 100644 --- a/comfy_api_nodes/nodes_bria.py +++ b/comfy_api_nodes/nodes_bria.py @@ -1,13 +1,16 @@ +import av +import torch +from av.codec import CodecContext from typing_extensions import override from comfy_api.latest import IO, ComfyExtension, Input from comfy_api_nodes.apis.bria import ( BriaEditImageRequest, + BriaImageEditResponse, BriaRemoveBackgroundRequest, BriaRemoveBackgroundResponse, BriaRemoveVideoBackgroundRequest, BriaRemoveVideoBackgroundResponse, - BriaImageEditResponse, BriaStatusResponse, InputModerationSettings, ) @@ -316,6 +319,96 @@ class BriaRemoveVideoBackground(IO.ComfyNode): return IO.NodeOutput(await download_url_to_video_output(response.result.video_url)) +def _video_to_images_and_mask(video: Input.Video) -> tuple[Input.Image, Input.Mask]: + """Decode a transparent webm (VP9 + alpha) into image frames and an alpha mask. + + VP9 keeps its alpha in a side layer that PyAV's default vp9 decoder drops, so the frames + are decoded with libvpx-vp9. Returns RGB images [B,H,W,3] in 0..1 and a mask [B,H,W] + following the Load Image convention (1 = transparent) for compositing or Save WEBM. + """ + rgb_frames: list[torch.Tensor] = [] + alpha_frames: list[torch.Tensor] = [] + with av.open(video.get_stream_source(), mode="r") as container: + stream = container.streams.video[0] + decoder = CodecContext.create("libvpx-vp9", "r") if stream.codec_context.name == "vp9" else None + for packet in container.demux(stream): + for frame in (decoder.decode(packet) if decoder is not None else packet.decode()): + rgba = torch.from_numpy(frame.to_ndarray(format="rgba")).float() / 255.0 + rgb_frames.append(rgba[..., :3]) + alpha_frames.append(rgba[..., 3]) + images = torch.stack(rgb_frames) if rgb_frames else torch.zeros(0, 0, 0, 3) + mask = (1.0 - torch.stack(alpha_frames)) if alpha_frames else torch.zeros((images.shape[0], 64, 64)) + return images, mask + + +class BriaTransparentVideoBackground(IO.ComfyNode): + + @classmethod + def define_schema(cls): + return IO.Schema( + node_id="BriaTransparentVideoBackground", + display_name="Bria Remove Video Background (Transparent)", + category="partner/video/Bria", + description="Remove the background from a video using Bria and return the cut-out frames " + "plus an alpha mask. Connect both to a compositing node, or feed them to Save WEBM to " + "write a transparent video.", + inputs=[ + IO.Video.Input("video"), + IO.Int.Input( + "seed", + default=0, + min=0, + max=2147483647, + display_mode=IO.NumberDisplay.number, + control_after_generate=True, + tooltip="Seed controls whether the node should re-run; " + "results are non-deterministic regardless of seed.", + ), + ], + outputs=[ + IO.Image.Output(display_name="images"), + IO.Mask.Output(display_name="mask"), + ], + hidden=[ + IO.Hidden.auth_token_comfy_org, + IO.Hidden.api_key_comfy_org, + IO.Hidden.unique_id, + ], + is_api_node=True, + price_badge=IO.PriceBadge( + expr="""{"type":"usd","usd":0.14,"format":{"suffix":"/second"}}""", + ), + ) + + @classmethod + async def execute( + cls, + video: Input.Video, + seed: int, + ) -> IO.NodeOutput: + validate_video_duration(video, max_duration=60.0) + response = await sync_op( + cls, + ApiEndpoint(path="/proxy/bria/v2/video/edit/remove_background", method="POST"), + data=BriaRemoveVideoBackgroundRequest( + video=await upload_video_to_comfyapi(cls, video), + background_color="Transparent", + output_container_and_codec="webm_vp9", + seed=seed, + ), + response_model=BriaStatusResponse, + ) + response = await poll_op( + cls, + ApiEndpoint(path=f"/proxy/bria/v2/status/{response.request_id}"), + status_extractor=lambda r: r.status, + response_model=BriaRemoveVideoBackgroundResponse, + ) + video_out = await download_url_to_video_output(response.result.video_url) + images, mask = _video_to_images_and_mask(video_out) + return IO.NodeOutput(images, mask) + + class BriaExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[IO.ComfyNode]]: @@ -323,6 +416,7 @@ class BriaExtension(ComfyExtension): BriaImageEditNode, BriaRemoveImageBackground, BriaRemoveVideoBackground, + BriaTransparentVideoBackground, ] diff --git a/comfy_extras/nodes_video.py b/comfy_extras/nodes_video.py index ae1d826d5..6f6c416a6 100644 --- a/comfy_extras/nodes_video.py +++ b/comfy_extras/nodes_video.py @@ -19,7 +19,7 @@ class SaveWEBM(io.ComfyNode): category="video", is_experimental=True, inputs=[ - io.Image.Input("images"), + io.Image.Input("images", tooltip="RGBA images are saved with their alpha channel as transparency (vp9 codec only)."), io.String.Input("filename_prefix", default="ComfyUI"), io.Combo.Input("codec", options=["vp9", "av1"]), io.Float.Input("fps", default=24.0, min=0.01, max=1000.0, step=0.01), @@ -45,18 +45,25 @@ class SaveWEBM(io.ComfyNode): for x in cls.hidden.extra_pnginfo: container.metadata[x] = json.dumps(cls.hidden.extra_pnginfo[x]) + # Save transparency when the images carry an alpha channel (RGBA) and the codec supports it. + # vp9 -> yuva420p; other codecs have no usable alpha path, so the alpha is ignored. + save_alpha = images.shape[-1] == 4 and codec == "vp9" + codec_map = {"vp9": "libvpx-vp9", "av1": "libsvtav1"} stream = container.add_stream(codec_map[codec], rate=Fraction(round(fps * 1000), 1000)) stream.width = images.shape[-2] stream.height = images.shape[-3] - stream.pix_fmt = "yuv420p10le" if codec == "av1" else "yuv420p" + stream.pix_fmt = "yuva420p" if save_alpha else ("yuv420p10le" if codec == "av1" else "yuv420p") stream.bit_rate = 0 stream.options = {'crf': str(crf)} if codec == "av1": stream.options["preset"] = "6" for frame in images: - frame = av.VideoFrame.from_ndarray(torch.clamp(frame[..., :3] * 255, min=0, max=255).to(device=torch.device("cpu"), dtype=torch.uint8).numpy(), format="rgb24") + if save_alpha: + frame = av.VideoFrame.from_ndarray(torch.clamp(frame[..., :4] * 255, min=0, max=255).to(device=torch.device("cpu"), dtype=torch.uint8).numpy(), format="rgba") + else: + frame = av.VideoFrame.from_ndarray(torch.clamp(frame[..., :3] * 255, min=0, max=255).to(device=torch.device("cpu"), dtype=torch.uint8).numpy(), format="rgb24") for packet in stream.encode(frame): container.mux(packet) container.mux(stream.encode()) From 4d360f9c9d33eb5b2a3e5d8fff8c2eca290a9960 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Thu, 4 Jun 2026 11:23:52 +0300 Subject: [PATCH 14/21] [Partner Nodes] fix (Seedance 2.0): prevent 1080p first/last-frame stretch jump (#14251) Signed-off-by: bigcat88 --- comfy_api_nodes/nodes_bytedance.py | 67 +++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/comfy_api_nodes/nodes_bytedance.py b/comfy_api_nodes/nodes_bytedance.py index d8885a7e5..c30ddc446 100644 --- a/comfy_api_nodes/nodes_bytedance.py +++ b/comfy_api_nodes/nodes_bytedance.py @@ -7,6 +7,7 @@ from io import BytesIO import torch from typing_extensions import override +from comfy.utils import common_upscale from comfy_api.latest import IO, ComfyExtension, Input, Types from comfy_api_nodes.apis.bytedance import ( RECOMMENDED_PRESETS, @@ -131,6 +132,44 @@ def _prepare_seedance_image(image: Input.Image) -> Input.Image: return image +# Supported output aspect ratios, used to pre-size FLF frames to matching pixel pair to avoid the 1080p stretch jump. +SEEDANCE2_RATIO_WH = { + "16:9": (16, 9), + "4:3": (4, 3), + "1:1": (1, 1), + "3:4": (3, 4), + "9:16": (9, 16), + "21:9": (21, 9), +} +SEEDANCE2_RES_SHORT_SIDE = {"480p": 480, "720p": 720, "1080p": 1080} + + +def _seedance2_target_dims(resolution: str, ratio: str, image: torch.Tensor) -> tuple[int, int]: + """Exact supported output (width, height) for (resolution, ratio). + + The shorter side equals the resolution number (e.g. 1080p 16:9 -> 1920x1080). For ratio + "adaptive" (or any unexpected value) the ratio is derived from the image's own aspect, snapped + to the nearest supported ratio, so the output keeps the frame's orientation. + """ + short = SEEDANCE2_RES_SHORT_SIDE[resolution] + if ratio not in SEEDANCE2_RATIO_WH: + aspect = image.shape[-2] / image.shape[-3] # W / H; tensor is (B, H, W, C) + ratio = min(SEEDANCE2_RATIO_WH, key=lambda k: abs(SEEDANCE2_RATIO_WH[k][0] / SEEDANCE2_RATIO_WH[k][1] - aspect)) + rw, rh = SEEDANCE2_RATIO_WH[ratio] + if rw >= rh: # landscape or square: shorter side is the height + out_w, out_h = round(short * rw / rh), short + else: # portrait: shorter side is the width + out_w, out_h = short, round(short * rh / rw) + return out_w - out_w % 2, out_h - out_h % 2 + + +def _resize_to_exact(image: torch.Tensor, width: int, height: int) -> torch.Tensor: + """Center-crop to the target aspect and resize to exactly width x height (lanczos).""" + samples = image.movedim(-1, 1) # (B, H, W, C) -> (B, C, H, W) + resized = common_upscale(samples, width, height, "lanczos", "center") + return resized.movedim(1, -1) + + async def _resolve_reference_assets( cls: type[IO.ComfyNode], asset_ids: list[str], @@ -1790,10 +1829,28 @@ class ByteDance2FirstLastFrameNode(IO.ComfyNode): if last_frame is not None and last_frame_asset_id: raise ValueError("Provide only one of last_frame or last_frame_asset_id, not both.") - if first_frame is not None: - first_frame = _prepare_seedance_image(first_frame) - if last_frame is not None: - last_frame = _prepare_seedance_image(last_frame) + request_ratio = model["ratio"] + if first_frame_asset_id or last_frame_asset_id: + if first_frame is not None: + first_frame = _prepare_seedance_image(first_frame) + if last_frame is not None: + last_frame = _prepare_seedance_image(last_frame) + else: + # The 1080p FLF stretch fix (pre-size frames to a supported pixel pair + submit ratio="adaptive") + # only applies to local image inputs we can resize. + request_ratio = "adaptive" + target_dims: tuple[int, int] | None = None + if first_frame is not None: + validate_image_aspect_ratio(first_frame, (2, 5), (5, 2), strict=False) # 0.4 to 2.5 + validate_image_dimensions(first_frame, min_width=300, min_height=300) + target_dims = _seedance2_target_dims(model["resolution"], model["ratio"], first_frame) + first_frame = _resize_to_exact(first_frame, *target_dims) + if last_frame is not None: + validate_image_aspect_ratio(last_frame, (2, 5), (5, 2), strict=False) # 0.4 to 2.5 + validate_image_dimensions(last_frame, min_width=300, min_height=300) + if target_dims is None: + target_dims = _seedance2_target_dims(model["resolution"], model["ratio"], last_frame) + last_frame = _resize_to_exact(last_frame, *target_dims) asset_ids_to_resolve = [a for a in (first_frame_asset_id, last_frame_asset_id) if a] image_assets: dict[str, str] = {} @@ -1844,7 +1901,7 @@ class ByteDance2FirstLastFrameNode(IO.ComfyNode): content=content, generate_audio=model["generate_audio"], resolution=model["resolution"], - ratio=model["ratio"], + ratio=request_ratio, duration=model["duration"], seed=seed, watermark=watermark, From 0a92dd9c096a490f861d6a173ea5f4302a73c613 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Thu, 4 Jun 2026 17:47:20 +0300 Subject: [PATCH 15/21] [Partner Nodes] feat: add Bria Green Background node (#14277) --- comfy_api_nodes/apis/bria.py | 25 ++++++ comfy_api_nodes/nodes_bria.py | 156 ++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/comfy_api_nodes/apis/bria.py b/comfy_api_nodes/apis/bria.py index e08a519a8..7a98428c3 100644 --- a/comfy_api_nodes/apis/bria.py +++ b/comfy_api_nodes/apis/bria.py @@ -97,3 +97,28 @@ class BriaRemoveVideoBackgroundResult(BaseModel): class BriaRemoveVideoBackgroundResponse(BaseModel): status: str = Field(...) result: BriaRemoveVideoBackgroundResult | None = Field(None) + + +class BriaVideoGreenScreenRequest(BaseModel): + video: str = Field(..., description="Publicly accessible URL of the input video.") + green_shade: str = Field( + default="broadcast_green", + description="Solid chroma-key shade applied behind the foreground " + "(broadcast_green, chroma_green, or blue_screen).", + ) + output_container_and_codec: str = Field(...) + preserve_audio: bool = Field(True) + seed: int = Field(...) + + +class BriaVideoReplaceBackgroundRequest(BaseModel): + video: str = Field(..., description="Publicly accessible URL of the input (foreground) video.") + background_url: str = Field( + ..., + description="Publicly accessible URL of the background image or video to composite behind " + "the foreground. Stretched to the foreground frame; match its aspect ratio for " + "undistorted results.", + ) + output_container_and_codec: str = Field(...) + preserve_audio: bool = Field(True) + seed: int = Field(...) diff --git a/comfy_api_nodes/nodes_bria.py b/comfy_api_nodes/nodes_bria.py index ce2c9e9be..e138fafa9 100644 --- a/comfy_api_nodes/nodes_bria.py +++ b/comfy_api_nodes/nodes_bria.py @@ -12,6 +12,8 @@ from comfy_api_nodes.apis.bria import ( BriaRemoveVideoBackgroundRequest, BriaRemoveVideoBackgroundResponse, BriaStatusResponse, + BriaVideoGreenScreenRequest, + BriaVideoReplaceBackgroundRequest, InputModerationSettings, ) from comfy_api_nodes.util import ( @@ -319,6 +321,158 @@ class BriaRemoveVideoBackground(IO.ComfyNode): return IO.NodeOutput(await download_url_to_video_output(response.result.video_url)) +class BriaVideoGreenScreen(IO.ComfyNode): + + @classmethod + def define_schema(cls): + return IO.Schema( + node_id="BriaVideoGreenScreen", + display_name="Bria Video Green Screen", + category="partner/video/Bria", + description="Replace a video's background with a solid chroma-key screen using Bria.", + inputs=[ + IO.Video.Input("video"), + IO.Combo.Input( + "green_shade", + options=["broadcast_green", "chroma_green", "blue_screen"], + tooltip="Solid chroma-key shade applied behind the foreground: " + "broadcast_green (#00B140), chroma_green (#00FF00), or blue_screen (#0000FF).", + ), + IO.Int.Input( + "seed", + default=0, + min=0, + max=2147483647, + display_mode=IO.NumberDisplay.number, + control_after_generate=True, + tooltip="Seed controls whether the node should re-run; " + "results are non-deterministic regardless of seed.", + ), + ], + outputs=[IO.Video.Output()], + hidden=[ + IO.Hidden.auth_token_comfy_org, + IO.Hidden.api_key_comfy_org, + IO.Hidden.unique_id, + ], + is_api_node=True, + price_badge=IO.PriceBadge( + expr="""{"type":"usd","usd":0.14,"format":{"suffix":"/second"}}""", + ), + ) + + @classmethod + async def execute( + cls, + video: Input.Video, + green_shade: str, + seed: int, + ) -> IO.NodeOutput: + validate_video_duration(video, max_duration=60.0) + response = await sync_op( + cls, + ApiEndpoint(path="/proxy/bria/v2/video/edit/green_screen", method="POST"), + data=BriaVideoGreenScreenRequest( + video=await upload_video_to_comfyapi(cls, video), + green_shade=green_shade, + output_container_and_codec="mp4_h264", + seed=seed, + ), + response_model=BriaStatusResponse, + ) + response = await poll_op( + cls, + ApiEndpoint(path=f"/proxy/bria/v2/status/{response.request_id}"), + status_extractor=lambda r: r.status, + response_model=BriaRemoveVideoBackgroundResponse, + ) + return IO.NodeOutput(await download_url_to_video_output(response.result.video_url)) + + +class BriaVideoReplaceBackground(IO.ComfyNode): + + @classmethod + def define_schema(cls): + return IO.Schema( + node_id="BriaVideoReplaceBackground", + display_name="Bria Video Replace Background", + category="partner/video/Bria", + description="Replace a video's background with a supplied image or video using Bria. " + "The output keeps the foreground's resolution and frame rate; a background with a " + "different aspect ratio is stretched to fit, so match it for undistorted results.", + inputs=[ + IO.Video.Input("video", tooltip="Foreground video whose background is replaced."), + IO.Image.Input( + "background_image", + optional=True, + tooltip="Background image to composite behind the foreground. " + "Provide either a background image or a background video, not both.", + ), + IO.Video.Input( + "background_video", + optional=True, + tooltip="Background video to composite behind the foreground. " + "Provide either a background image or a background video, not both.", + ), + IO.Int.Input( + "seed", + default=0, + min=0, + max=2147483647, + display_mode=IO.NumberDisplay.number, + control_after_generate=True, + tooltip="Seed controls whether the node should re-run; " + "results are non-deterministic regardless of seed.", + ), + ], + outputs=[IO.Video.Output()], + hidden=[ + IO.Hidden.auth_token_comfy_org, + IO.Hidden.api_key_comfy_org, + IO.Hidden.unique_id, + ], + is_api_node=True, + price_badge=IO.PriceBadge( + expr="""{"type":"usd","usd":0.14,"format":{"suffix":"/second"}}""", + ), + ) + + @classmethod + async def execute( + cls, + video: Input.Video, + seed: int, + background_image: Input.Image | None = None, + background_video: Input.Video | None = None, + ) -> IO.NodeOutput: + if (background_image is None) == (background_video is None): + raise ValueError("Provide either a background image or a background video, not both.") + validate_video_duration(video, max_duration=60.0) + if background_video is not None: + validate_video_duration(background_video, max_duration=60.0) + background_url = await upload_video_to_comfyapi(cls, background_video, wait_label="Uploading background") + else: + background_url = await upload_image_to_comfyapi(cls, background_image, wait_label="Uploading background") + response = await sync_op( + cls, + ApiEndpoint(path="/proxy/bria/v2/video/edit/replace_background", method="POST"), + data=BriaVideoReplaceBackgroundRequest( + video=await upload_video_to_comfyapi(cls, video), + background_url=background_url, + output_container_and_codec="mp4_h264", + seed=seed, + ), + response_model=BriaStatusResponse, + ) + response = await poll_op( + cls, + ApiEndpoint(path=f"/proxy/bria/v2/status/{response.request_id}"), + status_extractor=lambda r: r.status, + response_model=BriaRemoveVideoBackgroundResponse, + ) + return IO.NodeOutput(await download_url_to_video_output(response.result.video_url)) + + def _video_to_images_and_mask(video: Input.Video) -> tuple[Input.Image, Input.Mask]: """Decode a transparent webm (VP9 + alpha) into image frames and an alpha mask. @@ -416,6 +570,8 @@ class BriaExtension(ComfyExtension): BriaImageEditNode, BriaRemoveImageBackground, BriaRemoveVideoBackground, + BriaVideoGreenScreen, + # BriaVideoReplaceBackground, # server returns Status 500 when we pass background video BriaTransparentVideoBackground, ] From 1f9e7df52ac07ee6956500d02ba85ab09082e9f9 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:24:22 +0300 Subject: [PATCH 16/21] [Partner Nodes] feat: add Krea 2 Medium Turbo model (#14280) --- comfy_api_nodes/nodes_krea.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/comfy_api_nodes/nodes_krea.py b/comfy_api_nodes/nodes_krea.py index 34369f05f..b9e6268f2 100644 --- a/comfy_api_nodes/nodes_krea.py +++ b/comfy_api_nodes/nodes_krea.py @@ -42,9 +42,11 @@ async def _upload_image_to_krea_assets(cls: type[IO.ComfyNode], image: Input.Ima _MODEL_MEDIUM = "Krea 2 Medium" +_MODEL_MEDIUM_TURBO = "Krea 2 Medium Turbo" _MODEL_LARGE = "Krea 2 Large" _MODEL_ENDPOINTS: dict[str, str] = { _MODEL_MEDIUM: "/proxy/krea/generate/image/krea/krea-2/medium", + _MODEL_MEDIUM_TURBO: "/proxy/krea/generate/image/krea/krea-2/medium-turbo", _MODEL_LARGE: "/proxy/krea/generate/image/krea/krea-2/large", } @@ -57,7 +59,7 @@ _UUID_RE = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F def _krea_model_inputs() -> list: - """Nested inputs shared by both Krea 2 Medium and Large under the DynamicCombo.""" + """Nested inputs shared by Krea 2 Medium, Medium Turbo and Large under the DynamicCombo.""" return [ IO.Combo.Input( "aspect_ratio", @@ -123,6 +125,7 @@ class Krea2ImageNode(IO.ComfyNode): "model", options=[ IO.DynamicCombo.Option(_MODEL_MEDIUM, _krea_model_inputs()), + IO.DynamicCombo.Option(_MODEL_MEDIUM_TURBO, _krea_model_inputs()), IO.DynamicCombo.Option(_MODEL_LARGE, _krea_model_inputs()), ], tooltip="Krea 2 Medium is best for expressive illustrations; " @@ -151,14 +154,15 @@ class Krea2ImageNode(IO.ComfyNode): ), expr=""" ( - $isLarge := widgets.model = "krea 2 large"; + $rates := { + "krea 2 medium turbo": {"text": 0.015, "style": 0.0175, "moodboard": 0.02}, + "krea 2 medium": {"text": 0.03, "style": 0.035, "moodboard": 0.04}, + "krea 2 large": {"text": 0.06, "style": 0.065, "moodboard": 0.07} + }; + $r := $lookup($rates, widgets.model); $hasMoodboard := $length($lookup(widgets, "model.moodboard_id")) > 0; $hasStyle := $lookup(inputs, "model.style_reference").connected; - $usd := $hasMoodboard - ? ($isLarge ? 0.07 : 0.04) - : ($hasStyle - ? ($isLarge ? 0.065 : 0.035) - : ($isLarge ? 0.06 : 0.03)); + $usd := $hasMoodboard ? $r.moodboard : ($hasStyle ? $r.style : $r.text); {"type":"usd","usd": $usd} ) """, From 27b5c423a6ff44a0b90f879b05d58b9a17d86528 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Thu, 4 Jun 2026 19:32:15 +0300 Subject: [PATCH 17/21] [Partner Nodes] feat: add seed input to Flux Erase node (#14283) Signed-off-by: bigcat88 --- comfy_api_nodes/apis/bfl.py | 1 + comfy_api_nodes/nodes_bfl.py | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/comfy_api_nodes/apis/bfl.py b/comfy_api_nodes/apis/bfl.py index 2ad651122..4c950da84 100644 --- a/comfy_api_nodes/apis/bfl.py +++ b/comfy_api_nodes/apis/bfl.py @@ -43,6 +43,7 @@ class BFLFluxEraseRequest(BaseModel): "white (255) marks areas to remove, black (0) marks areas to preserve.", ) dilate_pixels: int = Field(10) + seed: int | None = Field(None) output_format: str = Field("png") diff --git a/comfy_api_nodes/nodes_bfl.py b/comfy_api_nodes/nodes_bfl.py index 79961ff9d..259c54ef9 100644 --- a/comfy_api_nodes/nodes_bfl.py +++ b/comfy_api_nodes/nodes_bfl.py @@ -534,6 +534,15 @@ class FluxEraseNode(IO.ComfyNode): max=25, tooltip="Expands the mask boundaries to ensure clean coverage of the object's edges.", ), + IO.Int.Input( + "seed", + default=0, + min=0, + max=2147483647, + control_after_generate=True, + tooltip="The random seed used for creating the noise.", + optional=True, + ), ], outputs=[IO.Image.Output()], hidden=[ @@ -553,6 +562,7 @@ class FluxEraseNode(IO.ComfyNode): image: Input.Image, mask: Input.Image, dilate_pixels: int = 10, + seed: int = 0, ) -> IO.NodeOutput: validate_image_dimensions(image, min_width=256, min_height=256) mask = resize_mask_to_image(mask, image) @@ -565,6 +575,7 @@ class FluxEraseNode(IO.ComfyNode): image=tensor_to_base64_string(image[:, :, :, :3]), # make sure image will have alpha channel removed mask=mask, dilate_pixels=dilate_pixels, + seed=seed, ), ) From 6ecca5f468ac5a24c450d601867de4d366f701fc Mon Sep 17 00:00:00 2001 From: "Daxiong (Lin)" Date: Fri, 5 Jun 2026 00:40:44 +0800 Subject: [PATCH 18/21] chore: update workflow templates to v0.9.98 (#14284) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 79d38fc06..6f88daafa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ comfyui-frontend-package==1.44.19 -comfyui-workflow-templates==0.9.94 +comfyui-workflow-templates==0.9.98 comfyui-embedded-docs==0.5.2 torch torchsde From 4e1f7cb1db1c26bb9ee61cf1875776517e2abae8 Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Fri, 5 Jun 2026 03:41:33 +0900 Subject: [PATCH 19/21] Bump comfyui-frontend-package to 1.45.15 (#14265) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6f88daafa..8b64c60a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -comfyui-frontend-package==1.44.19 +comfyui-frontend-package==1.45.15 comfyui-workflow-templates==0.9.98 comfyui-embedded-docs==0.5.2 torch From 514bb8ba21626da3126847321513f9863c88ce2c Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Thu, 4 Jun 2026 19:20:22 -0700 Subject: [PATCH 20/21] Fix ideogram if model dtype gets set to fp8. (#14291) --- comfy/ldm/ideogram4/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/comfy/ldm/ideogram4/model.py b/comfy/ldm/ideogram4/model.py index 3b02a243a..b86c65bf0 100644 --- a/comfy/ldm/ideogram4/model.py +++ b/comfy/ldm/ideogram4/model.py @@ -174,7 +174,7 @@ class Ideogram4Transformer(nn.Module): llm = self.llm_cond_proj(llm) * text_mask h[:, :L_text] = h[:, :L_text] + llm - h = h + self.embed_image_indicator((indicator == OUTPUT_IMAGE_INDICATOR).to(torch.long)) + h = h + self.embed_image_indicator((indicator == OUTPUT_IMAGE_INDICATOR).to(torch.long), out_dtype=h.dtype) # Qwen3-VL interleaved MRoPE; position_ids (B, L, 3) -> (3, L) (same across batch). freqs_cis = precompute_freqs_cis( @@ -235,7 +235,7 @@ class Ideogram4Transformer2DModel(Ideogram4Transformer): def _run_conditional(self, x_chunk, context_chunk, attn_mask_chunk, t_chunk, gh, gw, transformer_options): B = x_chunk.shape[0] device = x_chunk.device - img_tokens = self._img_to_tokens(x_chunk).to(self.dtype) + img_tokens = self._img_to_tokens(x_chunk) L_img = img_tokens.shape[1] L_text = context_chunk.shape[1] L = L_text + L_img @@ -268,7 +268,7 @@ class Ideogram4Transformer2DModel(Ideogram4Transformer): def _run_image_only(self, x_chunk, t_chunk, gh, gw, transformer_options): B = x_chunk.shape[0] device = x_chunk.device - img_tokens = self._img_to_tokens(x_chunk).to(self.dtype) + img_tokens = self._img_to_tokens(x_chunk) L_img = img_tokens.shape[1] position_ids = self._image_position_ids(gh, gw, device).unsqueeze(0).expand(B, L_img, 3) From ab0d8a9203fbad76b0ccca723bbf9ba0c257ddfe Mon Sep 17 00:00:00 2001 From: Alexis Rolland Date: Thu, 4 Jun 2026 19:29:41 -0700 Subject: [PATCH 21/21] Consolidate audio nodes into SaveAudioAdvanced node (CORE-202) (#13871) --- comfy_api/latest/_ui.py | 2 +- comfy_extras/nodes_audio.py | 58 +++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/comfy_api/latest/_ui.py b/comfy_api/latest/_ui.py index 6592f6b1d..b48713d41 100644 --- a/comfy_api/latest/_ui.py +++ b/comfy_api/latest/_ui.py @@ -285,7 +285,7 @@ class AudioSaveHelper: results = [] for batch_number, waveform in enumerate(audio["waveform"].cpu()): filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) - file = f"{filename_with_batch_num}_{counter:05}_.{format}" + file = f"{filename_with_batch_num}_{counter:05}.{format}" output_path = os.path.join(full_output_folder, file) # Use original sample rate initially diff --git a/comfy_extras/nodes_audio.py b/comfy_extras/nodes_audio.py index ff078f74c..1dc97ecd7 100644 --- a/comfy_extras/nodes_audio.py +++ b/comfy_extras/nodes_audio.py @@ -158,7 +158,7 @@ class SaveAudio(IO.ComfyNode): return IO.Schema( node_id="SaveAudio", search_aliases=["export flac"], - display_name="Save Audio (FLAC)", + display_name="Save Audio (FLAC) (Deprecated)", category="audio", essentials_category="Audio", inputs=[ @@ -167,6 +167,7 @@ class SaveAudio(IO.ComfyNode): ], hidden=[IO.Hidden.prompt, IO.Hidden.extra_pnginfo], is_output_node=True, + is_deprecated=True, ) @classmethod @@ -186,7 +187,7 @@ class SaveAudioMP3(IO.ComfyNode): return IO.Schema( node_id="SaveAudioMP3", search_aliases=["export mp3"], - display_name="Save Audio (MP3)", + display_name="Save Audio (MP3) (Deprecated)", category="audio", essentials_category="Audio", inputs=[ @@ -196,6 +197,7 @@ class SaveAudioMP3(IO.ComfyNode): ], hidden=[IO.Hidden.prompt, IO.Hidden.extra_pnginfo], is_output_node=True, + is_deprecated=True, ) @classmethod @@ -217,7 +219,7 @@ class SaveAudioOpus(IO.ComfyNode): return IO.Schema( node_id="SaveAudioOpus", search_aliases=["export opus"], - display_name="Save Audio (Opus)", + display_name="Save Audio (Opus) (Deprecated)", category="audio", inputs=[ IO.Audio.Input("audio"), @@ -226,6 +228,7 @@ class SaveAudioOpus(IO.ComfyNode): ], hidden=[IO.Hidden.prompt, IO.Hidden.extra_pnginfo], is_output_node=True, + is_deprecated=True, ) @classmethod @@ -241,6 +244,54 @@ class SaveAudioOpus(IO.ComfyNode): save_opus = execute # TODO: remove +class SaveAudioAdvanced(IO.ComfyNode): + @classmethod + def define_schema(cls): + return IO.Schema( + node_id="SaveAudioAdvanced", + search_aliases=["save audio", "export audio", "output audio", "write audio", "flac", "mp3", "opus"], + display_name="Save Audio (Advanced)", + description="Saves the input audio to your ComfyUI output directory.", + category="audio", + inputs=[ + IO.Audio.Input("audio", tooltip="The audio to save."), + IO.String.Input( + "filename_prefix", + default="audio/ComfyUI", + tooltip=( + "The prefix for the file to save. May include formatting tokens " + "such as %date:yyyy-MM-dd%." + ), + ), + IO.DynamicCombo.Input( + "format", + options=[ + IO.DynamicCombo.Option("flac", []), + IO.DynamicCombo.Option("mp3", [ + IO.Combo.Input("quality", options=["V0", "128k", "320k"], default="V0"), + ]), + IO.DynamicCombo.Option("opus", [ + IO.Combo.Input("quality", options=["64k", "96k", "128k", "192k", "320k"], default="128k"), + ]), + ], + tooltip="The file format in which to save the audio.", + ), + ], + hidden=[IO.Hidden.prompt, IO.Hidden.extra_pnginfo], + is_output_node=True, + ) + + @classmethod + def execute(cls, audio, filename_prefix: str, format: dict) -> IO.NodeOutput: + file_format = format.get("format", None) + quality = format.get("quality", None) + if quality: + ui=UI.AudioSaveHelper.get_save_audio_ui(audio, filename_prefix=filename_prefix, cls=cls, format=file_format, quality=quality) + else: + ui=UI.AudioSaveHelper.get_save_audio_ui(audio, filename_prefix=filename_prefix, cls=cls, format=file_format) + return IO.NodeOutput(ui=ui) + + class PreviewAudio(IO.ComfyNode): @classmethod def define_schema(cls): @@ -822,6 +873,7 @@ class AudioExtension(ComfyExtension): SaveAudio, SaveAudioMP3, SaveAudioOpus, + SaveAudioAdvanced, LoadAudio, PreviewAudio, ConditioningStableAudio,