Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions development/cloud/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
### Upload Mask

<Note>
The `subfolder` parameter is accepted for API compatibility but ignored in cloud storage. All files are stored in a flat, content-addressed namespace.

Check warning on line 186 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L186

Did you really mean 'namespace'?
</Note>

<CodeGroup>
Expand Down Expand Up @@ -409,8 +409,8 @@
```typescript TypeScript
function setWorkflowInput(
workflow: Record<string, any>,
nodeId: string,

Check warning on line 412 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L412

Did you really mean 'nodeId'?
inputName: string,

Check warning on line 413 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L413

Did you really mean 'inputName'?
value: any
): Record<string, any> {
if (workflow[nodeId]) {
Expand Down Expand Up @@ -450,6 +450,196 @@

---

## Jobs API

The Jobs API provides efficient endpoints for listing and retrieving job details. Use these endpoints instead of the legacy `/history` and `/queue` endpoints for better performance and richer filtering options.

### List Jobs

Retrieve a paginated list of jobs with optional filtering by status, workflow ID, or output type.

<CodeGroup>
```bash curl
# List all jobs (most recent first)
curl -X GET "$BASE_URL/api/jobs" \
-H "X-API-Key: $COMFY_CLOUD_API_KEY"

# Filter by status
curl -X GET "$BASE_URL/api/jobs?status=completed" \
-H "X-API-Key: $COMFY_CLOUD_API_KEY"

# Filter by output type and sort by execution time
curl -X GET "$BASE_URL/api/jobs?output_type=image&sort_by=execution_time&sort_order=desc" \
-H "X-API-Key: $COMFY_CLOUD_API_KEY"
```

```typescript TypeScript
interface JobEntry {
id: string;
status: "pending" | "in_progress" | "completed" | "failed" | "cancelled";
create_time: number;
preview_output?: Record<string, any>;
outputs_count?: number;
workflow_id?: string;
execution_start_time?: number;
execution_end_time?: number;
}

interface JobsListResponse {
jobs: JobEntry[];
pagination: {
offset: number;
limit: number;
total: number;
has_more: boolean;
};
}

async function listJobs(options: {
status?: string;
output_type?: "image" | "video" | "audio";
Copy link
Copy Markdown
Contributor

@MillerMedia MillerMedia May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output_type enum is missing "3d".

The live API accepts a fourth value, "3d", in addition to image | video | audio. The TypeScript signature here drops it, so anyone copy-pasting this type into their own SDK will get a compile error the moment they try to filter for 3D outputs.

-  output_type?: "image" | "video" | "audio";
+  output_type?: "image" | "video" | "audio" | "3d";

The Python docstring on line 539 has the same issue.

sort_by?: "create_time" | "execution_time";
sort_order?: "asc" | "desc";
offset?: number;
limit?: number;
} = {}): Promise<JobsListResponse> {
const params = new URLSearchParams();
if (options.status) params.set("status", options.status);
if (options.output_type) params.set("output_type", options.output_type);
if (options.sort_by) params.set("sort_by", options.sort_by);
if (options.sort_order) params.set("sort_order", options.sort_order);
if (options.offset !== undefined) params.set("offset", String(options.offset));
if (options.limit !== undefined) params.set("limit", String(options.limit));

const response = await fetch(`${BASE_URL}/api/jobs?${params}`, {
headers: getHeaders(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getHeaders() / get_headers() are referenced but never defined.

The TypeScript example on this line calls getHeaders(); the Python equivalent on line 561 calls get_headers(); the curl block above (line 465) uses -H "X-API-Key: $COMFY_CLOUD_API_KEY". Three different auth conventions in one section, and the two helper-function ones are phantom — nothing in the diff (or earlier on the page) introduces them.

A developer copy-pasting these snippets gets a ReferenceError/NameError. Either define the helpers up front in the page (a one-liner that returns { "X-API-Key": ... }) or inline the header inside each snippet so it matches the curl block.

Same issue at line 600 (getJobDetails) and line 625 (get_job_details).

});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}

// List recent completed jobs
const { jobs, pagination } = await listJobs({ status: "completed", limit: 10 });
console.log(`Found ${pagination.total} completed jobs`);
```

```python Python
def list_jobs(
status: str = None,
output_type: str = None,
sort_by: str = "create_time",
sort_order: str = "desc",
offset: int = 0,
limit: int = 100
) -> dict:
"""List jobs with optional filtering.

Args:
status: Filter by status (pending, in_progress, completed, failed, cancelled)
output_type: Filter by output type (image, video, audio)
sort_by: Sort field (create_time or execution_time)
sort_order: Sort direction (asc or desc)
offset: Pagination offset
limit: Max items per page (1-1000)

Returns:
Dict with 'jobs' array and 'pagination' info
"""
params = {
"sort_by": sort_by,
"sort_order": sort_order,
"offset": offset,
"limit": limit
}
if status:
params["status"] = status
if output_type:
params["output_type"] = output_type

response = requests.get(
f"{BASE_URL}/api/jobs",
headers=get_headers(),
params=params
)
response.raise_for_status()
return response.json()

# List recent completed jobs
result = list_jobs(status="completed", limit=10)
print(f"Found {result['pagination']['total']} completed jobs")
```
</CodeGroup>

### Get Job Details

Retrieve complete details for a specific job including the workflow and full outputs.

<CodeGroup>
```bash curl
curl -X GET "$BASE_URL/api/jobs/{job_id}" \
-H "X-API-Key: $COMFY_CLOUD_API_KEY"
```

```typescript TypeScript
interface JobDetailResponse {
id: string;
status: "pending" | "in_progress" | "completed" | "failed" | "cancelled";
workflow?: Record<string, any>;
Copy link
Copy Markdown
Contributor

@MillerMedia MillerMedia May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workflow is typed as Record<string, any> and hides a real footgun.

When the API returns a workflow, sensitive credentials inside extra_data are redacted before the response is sent. Specifically: if the original submission contained extra_data.api_key_comfy_org, the returned value of that field is replaced with the literal string "[REDACTED]". The field is preserved (not removed), so existence checks still pass, but the value is not usable.

A developer reading these docs and round-tripping a returned workflow back into /api/prompt will silently submit "[REDACTED]" as their API key and then debug a confusing auth failure. This is exactly the case where the type comment matters more than the type itself.

At minimum, add a short callout above this interface: "Returned workflows have extra_data.api_key_comfy_org replaced with "[REDACTED]" — strip or replace this field before resubmitting."

outputs?: Record<string, any>;
preview_output?: Record<string, any>;
outputs_count?: number;
create_time: number;
update_time: number;
workflow_id?: string;
execution_status?: Record<string, any>;
execution_meta?: Record<string, any>;
}

async function getJobDetails(jobId: string): Promise<JobDetailResponse> {
const response = await fetch(`${BASE_URL}/api/jobs/${jobId}`, {
headers: getHeaders(),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}

// Get full job details including outputs
const job = await getJobDetails(promptId);
Copy link
Copy Markdown
Contributor

@MillerMedia MillerMedia May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

promptId is undefined, and the example silently equates prompt-id with job-id.

promptId (here) and prompt_id (Python, line 631) aren't introduced anywhere on the page. The endpoint is /api/jobs/{job_id} and the path parameter is job_id, but the example passes a variable called promptId — the only reason this works is that the cloud platform happens to use the same UUID for both.

This page never says they're the same value. A developer who got their prompt id from /api/prompt will reasonably assume they need to do a separate lookup to get a job id. Either:

  • Rename the variable to jobId/job_id and show how to obtain it (e.g. const jobId = (await fetch('/api/prompt'…)).prompt_id), or
  • Add a one-line callout: "The prompt_id returned by /api/prompt is the same as job_id."

(The JSON example response above also uses "id": "prompt-uuid" rather than a real UUID, which compounds the confusion — the id field is a UUID in the actual response.)

if (job.status === "completed" && job.outputs) {
console.log(`Job has ${job.outputs_count} outputs`);
}
```

```python Python
def get_job_details(job_id: str) -> dict:
"""Get complete job details including workflow and outputs.

Args:
job_id: The job/prompt ID

Returns:
Full job details including workflow and outputs
"""
response = requests.get(
f"{BASE_URL}/api/jobs/{job_id}",
headers=get_headers()
)
response.raise_for_status()
return response.json()

# Get full job details including outputs
job = get_job_details(prompt_id)
if job["status"] == "completed" and job.get("outputs"):
print(f"Job has {job.get('outputs_count', 0)} outputs")
```
</CodeGroup>

<Note>
**Legacy Endpoints:** The `/api/history`, `/api/history_v2`, and `/api/queue` endpoints are maintained for compatibility with local ComfyUI but the Jobs API is recommended for new integrations.
Copy link
Copy Markdown
Contributor

@MillerMedia MillerMedia May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compatibility justification is factually wrong, and undersells the actual deprecation status.

Two issues:

  1. "maintained for compatibility with local ComfyUI" — local ComfyUI's API surface is /history, /history/{prompt_id}, /queue. There is no /api/ prefix and no _v2 variant. /api/history_v2 is a cloud-only endpoint that has no analog in local ComfyUI. So the stated reason for keeping the legacy surface doesn't hold.

  2. "maintained" is softer than reality. These endpoints are deprecated, and some of them already return 404 redirecting callers to /api/jobs/{prompt_id} rather than serving the legacy response. A reader of this Note may build new integrations on endpoints that already 404.

Suggested rewrite:

Legacy Endpoints: /api/history, /api/history_v2, and /api/queue are deprecated and may already return 404 — new integrations must use the Jobs API. Existing integrations should migrate; see migration mapping for field equivalences.

</Note>

---

## Checking Job Status

Poll for job completion.
Expand Down Expand Up @@ -511,7 +701,7 @@
| Offset | Size | Field | Description |
|--------|------|-------|-------------|
| 0 | 4 bytes | `type` | `0x00000003` |
| 4 | 4 bytes | `node_id_len` | Length of node_id string |

Check warning on line 704 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L704

Did you really mean 'node_id'?
| 8 | N bytes | `node_id` | UTF-8 encoded node ID |
| 8+N | variable | `text` | UTF-8 encoded progress text |
</Tab>
Expand Down Expand Up @@ -575,7 +765,7 @@
}

async function waitForCompletion(
promptId: string,

Check warning on line 768 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L768

Did you really mean 'promptId'?
timeout: number = 300000
): Promise<Record<string, any>> {
const wsUrl = `wss://cloud.comfy.org/ws?clientId=${crypto.randomUUID()}&token=${API_KEY}`;
Expand Down Expand Up @@ -619,7 +809,7 @@

async function downloadOutputs(
outputs: Record<string, any>,
outputDir: string

Check warning on line 812 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L812

Did you really mean 'outputDir'?
): Promise<void> {
for (const nodeOutputs of Object.values(outputs)) {
for (const key of ["images", "video", "audio"]) {
Expand All @@ -635,9 +825,9 @@
redirect: "manual",
});
if (response.status !== 302) throw new Error(`HTTP ${response.status}`);
const signedUrl = response.headers.get("location")!;

Check warning on line 828 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L828

Did you really mean 'signedUrl'?
// Fetch from signed URL without auth headers
const fileResponse = await fetch(signedUrl);

Check warning on line 830 in development/cloud/api-reference.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/cloud/api-reference.mdx#L830

Did you really mean 'fileResponse'?
if (!fileResponse.ok) throw new Error(`HTTP ${fileResponse.status}`);

const path = `${outputDir}/${fileInfo.filename}`;
Expand Down
73 changes: 70 additions & 3 deletions development/comfyui-server/comms_routes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@
| `/upload/mask` | post | upload a mask |
| `/view` | get | view an image. Lots of options, see `@routes.get("/view")` in `server.py` |
| `/view_metadata`/{folder_name} | get | retrieve metadata for a model |
| `/system_stats` | get | retrieve information about the system (python version, devices, vram etc) |

Check warning on line 33 in development/comfyui-server/comms_routes.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/comfyui-server/comms_routes.mdx#L33

Did you really mean 'vram'?
| `/prompt` | get | retrieve current queue status and execution information |
| `/prompt` | post | submit a prompt to the queue |
| `/object_info` | get | retrieve details of all node types |
| `/object_info/{node_class}` | get | retrieve details of one node type |
| `/history` | get | retrieve the queue history |
| `/history/{prompt_id}` | get | retrieve the queue history for a specific prompt |
| `/jobs` | get | list jobs with filtering, sorting, and pagination (recommended) |
| `/jobs/{job_id}` | get | get full details for a specific job (recommended) |
| `/history` | get | retrieve the queue history (legacy) |
| `/history/{prompt_id}` | get | retrieve the queue history for a specific prompt (legacy) |
| `/history` | post | clear history or delete history item |
| `/queue` | get | retrieve the current state of the execution queue |
| `/queue` | get | retrieve the current state of the execution queue (legacy) |
| `/queue` | post | manage queue operations (clear pending/running) |
| `/interrupt` | post | stop the current workflow execution |
| `/free` | post | free memory by unloading specified models |
Expand All @@ -51,6 +53,71 @@
| `/users` | get | get user information |
| `/users` | post | create a new user (multi-user mode only) |

### Jobs API

The Jobs API (`/jobs`) provides a unified interface for listing and retrieving job information with filtering, sorting, and pagination. It is the recommended way to query job status and history.

#### List Jobs

`GET /jobs` returns a paginated list of jobs with optional filtering:

| Parameter | Type | Description |
|-----------|------|-------------|
| `status` | string | Filter by status (comma-separated): `pending`, `in_progress`, `completed`, `failed`, `cancelled` |
| `workflow_id` | string | Filter by workflow ID |
| `sort_by` | string | Sort field: `created_at` (default), `execution_duration` |
Copy link
Copy Markdown
Contributor

@MillerMedia MillerMedia May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sort_by values contradict api-reference.mdx in this same PR (and the actual API).

The live cloud API accepts sort_by values create_time (default) and execution_timeapi-reference.mdx (lines 472, 501, 540) uses those values. This row says created_at and execution_duration, neither of which is accepted server-side.

A reader following this table will send sort_by=created_at and get either a 400 or a silently-ignored sort. Should be:

| `sort_by` | string | Sort field: `create_time` (default), `execution_time` |

| `sort_order` | string | Sort direction: `asc`, `desc` (default) |
| `limit` | integer | Maximum items to return |
| `offset` | integer | Items to skip (default: 0) |

**Response:**
```json
{
"jobs": [
{
"id": "prompt-uuid",
"status": "completed",
"create_time": 1706540000,
"workflow_id": "workflow-uuid",
"outputs_count": 2,
"preview_output": {"filename": "output.png", "type": "output"},
"execution_start_time": 1706540001000,

Check warning on line 84 in development/comfyui-server/comms_routes.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/comfyui-server/comms_routes.mdx#L84

Did you really mean 'execution_start_time'?
Comment on lines +80 to +84
Copy link
Copy Markdown
Contributor

@MillerMedia MillerMedia May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timestamp units mismatched in the same payload, and create_time is the wrong unit.

The API actually returns create_time in milliseconds (consistent with execution_start_time and execution_end_time). The example here shows 1706540000 (10 digits = seconds), while execution_start_time in the same object shows 1706540001000 (13 digits = milliseconds).

Clients parsing this as an example will either:

  • Treat both fields the same and be off by 1000×, or
  • Treat create_time as ms (matching the actual response) and display 1970 dates.

Fix the example to use 13-digit ms throughout: "create_time": 1706540000000.

"execution_end_time": 1706540005000

Check warning on line 85 in development/comfyui-server/comms_routes.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/comfyui-server/comms_routes.mdx#L85

Did you really mean 'execution_end_time'?
}
],
"pagination": {
"offset": 0,
"limit": 100,
"total": 42,
"has_more": false
}
}
```

#### Get Job Details

`GET /jobs/{job_id}` returns full details for a specific job including outputs and workflow:

```json
{
"id": "prompt-uuid",
"status": "completed",
"create_time": 1706540000,
"outputs": {
"9": {"images": [{"filename": "output.png", "type": "output"}]}
},
"workflow": {
"prompt": {...},
"extra_data": {...}
},
"execution_status": {...}
}
```

<Note>
The Jobs API consolidates data from the queue and history into a single unified format. Use `/jobs` instead of separately querying `/queue` and `/history` endpoints.
</Note>

### WebSocket Communication

The `/ws` endpoint provides real-time bidirectional communication between the client and server. This is used for:
Expand Down Expand Up @@ -87,7 +154,7 @@

<Tip>Unless you know what you are doing, don't try to define `my_function` within a class.
The `@routes.post` decorator does a lot of work! Instead, define the function as above
and then call a classmethod.</Tip>

Check warning on line 157 in development/comfyui-server/comms_routes.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

development/comfyui-server/comms_routes.mdx#L157

Did you really mean 'classmethod'?

<Tip>You can also define a `@routes.get` if you aren't changing anything.</Tip>

Expand Down
4 changes: 2 additions & 2 deletions snippets/cloud/complete-example.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@
await new Promise((resolve) => setTimeout(resolve, 2000));
}

// 4. Get outputs via job detail endpoint
// 4. Get outputs via Jobs API
const jobRes = await fetch(`${BASE_URL}/api/jobs/${prompt_id}`, {

Check warning on line 38 in snippets/cloud/complete-example.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

snippets/cloud/complete-example.mdx#L38

Did you really mean 'jobRes'?
headers: { "X-API-Key": API_KEY },
});
const job = await jobRes.json();
const outputs = job.outputs;

// 5. Download output files
for (const nodeOutputs of Object.values(outputs)) {

Check warning on line 45 in snippets/cloud/complete-example.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

snippets/cloud/complete-example.mdx#L45

Did you really mean 'nodeOutputs'?
for (const fileInfo of (nodeOutputs as any).images ?? []) {
const params = new URLSearchParams({
filename: fileInfo.filename,
subfolder: fileInfo.subfolder ?? "",
type: "output",
});
const viewRes = await fetch(`${BASE_URL}/api/view?${params}`, {

Check warning on line 52 in snippets/cloud/complete-example.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

snippets/cloud/complete-example.mdx#L52

Did you really mean 'viewRes'?
headers: { "X-API-Key": API_KEY },
redirect: "manual",
});
Expand Down Expand Up @@ -104,7 +104,7 @@
raise RuntimeError(f"Job {status}")
time.sleep(2)

# 4. Get outputs via job detail endpoint
# 4. Get outputs via Jobs API
job_res = requests.get(
f"{BASE_URL}/api/jobs/{prompt_id}",
headers={"X-API-Key": API_KEY}
Expand Down
4 changes: 2 additions & 2 deletions snippets/zh/cloud/complete-example.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@
await new Promise((resolve) => setTimeout(resolve, 2000));
}

// 4. 通过任务详情端点获取输出
// 4. 通过 Jobs API 获取输出
const jobRes = await fetch(`${BASE_URL}/api/jobs/${prompt_id}`, {

Check warning on line 38 in snippets/zh/cloud/complete-example.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

snippets/zh/cloud/complete-example.mdx#L38

Did you really mean 'jobRes'?
headers: { "X-API-Key": API_KEY },
});
const job = await jobRes.json();
const outputs = job.outputs;

// 5. 下载输出文件
for (const nodeOutputs of Object.values(outputs)) {

Check warning on line 45 in snippets/zh/cloud/complete-example.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

snippets/zh/cloud/complete-example.mdx#L45

Did you really mean 'nodeOutputs'?
for (const fileInfo of (nodeOutputs as any).images ?? []) {
const params = new URLSearchParams({
filename: fileInfo.filename,
subfolder: fileInfo.subfolder ?? "",
type: "output",
});
const viewRes = await fetch(`${BASE_URL}/api/view?${params}`, {

Check warning on line 52 in snippets/zh/cloud/complete-example.mdx

View check run for this annotation

Mintlify / Mintlify Validation (dripart) - vale-spellcheck

snippets/zh/cloud/complete-example.mdx#L52

Did you really mean 'viewRes'?
headers: { "X-API-Key": API_KEY },
redirect: "manual",
});
Expand Down Expand Up @@ -104,7 +104,7 @@
raise RuntimeError(f"任务 {status}")
time.sleep(2)

# 4. 通过任务详情端点获取输出
# 4. 通过 Jobs API 获取输出
job_res = requests.get(
f"{BASE_URL}/api/jobs/{prompt_id}",
headers={"X-API-Key": API_KEY}
Expand Down
Loading
Loading