> ## Documentation Index
> Fetch the complete documentation index at: https://docs.comfy.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 대기 중 전달

> Comfy Router 요청을 제출하면 즉시 요청 ID를 돌려받고, 이후 상태를 추적하며 결과를 수집하거나 취소할 수 있습니다. Python, TypeScript, cURL에서 SDK의 submit, subscribe, handle을 사용하는 방법을 다룹니다.

`POST /v2/models/{provider}/{model}`는 모델이 끝날 때까지 연결을 유지합니다. 대기 중 전달은 동일한 모델 ID와 동일한 네이티브 요청 본문을 사용하지만, Router가 실행을 수락하는 즉시 반환합니다. `request_id`를 곧바로 돌려받고, 결과가 준비되면 같은 프로세스에서든 다른 프로세스에서든 수집하면 됩니다.

생성이 유지할 수 있는 연결보다 오래 걸릴 수 있을 때, 웹 요청이 지금 반환되어야 할 때, 한 프로세스에서 제출하고 다른 프로세스에서 수집할 때, 또는 여러 생성을 동시에 진행하고 싶을 때 실행 대기열을 사용하세요. 순서, 수락, 재시도, 타임아웃, 과금 및 만료는 모두 서버에서 결정됩니다. SDK는 그 위에 폴링과 편의 기능을 더할 뿐, 그 외에는 아무것도 하지 않습니다.

## 두 가지 전달 모드, 하나의 요청

|    | 동기식                                  | 대기 중                                          |
| -- | ------------------------------------ | --------------------------------------------- |
| 경로 | `POST /v2/models/{provider}/{model}` | `POST /v2/models/{provider}/{model}/requests` |
| 응답 | 모델의 네이티브 출력과 함께 `200`                | `request_id`와 세 개의 URL과 함께 `201`              |
| 결과 | 응답에 포함                               | 나중에 수집되며, 바이트 단위로 완전히 동일한 출력                  |

SDK(`comfy-sdk` 및 `@comfyorg/sdk`, 0.3.0 이상)는 실행 대기열을 `run` 옆의 세 가지 메서드로 노출합니다:

* \*\*`submit(model, body)`\*\*는 요청을 전송하고 즉시 핸들을 반환합니다. 핸들은 `status()`, `get()`, `cancel()` 및 이벤트 반복자(Python에서는 `iter_events()`, TypeScript에서는 `events()`)를 제공합니다.
* \*\*`subscribe(model, body, ...)`\*\*는 제출, 폴링, 수집을 한 번의 호출로 수행하며, 진행률 콜백을 함께 제공합니다.
* \*\*`handle(model, request_id)`\*\*는 호출 없이 두 개의 ID로 다른 프로세스에서 핸들을 재구성합니다.

두 ID 모두 요청을 지정하기 때문에 어디서나 두 ID가 필요합니다: 경로는 `/v2/models/{provider}/{model}/requests/{request_id}`입니다.

## 네 가지 라우트

| 라우트                                                              | 응답                                                                                             |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `POST /v2/models/{provider}/{model}/requests`                    | `201` 응답과 `request_id`, `status`, `queue_position`, `status_url`, `response_url`, `cancel_url` |
| `GET /v2/models/{provider}/{model}/requests/{request_id}/status` | 현재 `status`와 `queue_position`을 담은 `200`, 그리고 `Retry-After` 힌트                                  |
| `GET /v2/models/{provider}/{model}/requests/{request_id}`        | 완료되면 모델의 네이티브 출력을 담은 `200`, 아직 완료되지 않았으면 상태 본문을 담은 `202`                                       |
| `PUT /v2/models/{provider}/{model}/requests/{request_id}/cancel` | `202` `CANCELLATION_REQUESTED` 또는 `409` `ALREADY_COMPLETED`                                    |

`status`는 `IN_QUEUE`, `IN_PROGRESS`, `COMPLETED` 중 하나입니다. 별도의 실패 또는 취소됨 상태는 없습니다. 성공하지 못한 요청은 `error_type`을 포함한 `COMPLETED`이므로, 네 번째 상태 값이 아니라 해당 필드의 존재 여부를 기준으로 분기하세요. SDK가 이를 대신 처리합니다. `get()`은 실패를 결과로 돌려주는 대신 타입이 지정된 Router 오류를 발생시키거나 거부합니다.

진행 이벤트, 웹훅, 우선순위 수준은 없습니다. 요청을 추적하는 방법은 status 라우트입니다. [API 참조](/ko/development/comfy-router/reference#엔드포인트)에 각 라우트의 전체 계약이 나와 있습니다.

## Queue a request

This queues the same request the [quickstart](/ko/development/comfy-router/quickstart) sends and collects the image. Export your key as `COMFY_API_KEY` first.

<CodeGroup>
  ```python Python theme={null}
  from comfy_sdk import Comfy

  # Reads COMFY_API_KEY from the environment.
  # Each submit() call mints its own Idempotency-Key and reuses it for automatic retries.
  with Comfy() as client:
      handle = client.models.submit(
          "bfl/flux-2-pro",
          {"prompt": "a red teapot on a windowsill, morning light"},
      )
      print("request_id:", handle.request_id)  # with the model ID, all another process needs

      # Poll until the request completes, waiting the Retry-After the server names.
      for update in handle.iter_events():
          print(update.status, update.queue_position)

      # The provider's own payload, the same value models.run() returns.
      # A request that failed or was cancelled raises the typed Router error here.
      result = handle.get()

  print("image:", result["result"]["sample"])
  ```

  ```typescript TypeScript theme={null}
  import { comfy } from "@comfyorg/sdk";

  // Reads COMFY_API_KEY from the environment.
  // Each submit() call mints its own Idempotency-Key and reuses it for automatic retries.
  type FluxResult = { result: { sample: string } };
  const handle = await comfy.models.submit<FluxResult>("bfl/flux-2-pro", {
    prompt: "a red teapot on a windowsill, morning light",
  });
  console.log("requestId:", handle.requestId); // with the model ID, all another process needs

  // Poll until the request completes, waiting the Retry-After the server names.
  for await (const update of handle.events()) {
    console.log(update.status, update.queuePosition);
  }

  // The same result models.run() returns. A request that failed or was cancelled rejects here.
  const result = await handle.get();
  if (result.kind !== "json") throw new Error("expected a JSON result");

  console.log("image:", result.data.result.sample);
  ```

  ```bash cURL theme={null}
  BASE="https://api.comfy.org/v2/models/bfl/flux-2-pro"

  # 1. Submit. Router answers 201 with request_id, status_url, response_url and cancel_url.
  curl "$BASE/requests" \
    -H "X-API-Key: $COMFY_API_KEY" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{"prompt": "a red teapot on a windowsill, morning light"}'

  # 2. Poll until status is COMPLETED, waiting the Retry-After seconds each response names.
  REQUEST_ID="<request_id from the 201 body>"
  curl -i "$BASE/requests/$REQUEST_ID/status" -H "X-API-Key: $COMFY_API_KEY"

  # 3. Collect. 200 with the model's native output, 202 with the status body while it is still running.
  curl "$BASE/requests/$REQUEST_ID" -H "X-API-Key: $COMFY_API_KEY"

  # 4. Cancel a request that has not finished. A request, not a guarantee.
  curl -X PUT "$BASE/requests/$REQUEST_ID/cancel" -H "X-API-Key: $COMFY_API_KEY"
  ```
</CodeGroup>

Every [model page](/ko/development/comfy-router/models) carries this shape for its own model under **Queue and collect later**, beside the synchronous snippet.

### Follow progress and collect in one call

When you do want to wait but also want to show progress, `subscribe` folds submit, poll and collect into one call:

<CodeGroup>
  ```python Python theme={null}
  def on_update(update):
      print(update.status, update.queue_position)

  result = client.models.subscribe(
      "bfl/flux-2-pro",
      {"prompt": "a red teapot on a windowsill, morning light"},
      on_queue_update=on_update,
      timeout=300,
  )
  ```

  ```typescript TypeScript theme={null}
  const result = await comfy.models.subscribe<FluxResult>(
    "bfl/flux-2-pro",
    { prompt: "a red teapot on a windowsill, morning light" },
    {
      onQueueUpdate: (update) => console.log(update.status, update.queuePosition),
      timeoutMs: 300_000,
    },
  );
  ```
</CodeGroup>

The timeout is a client-side bound with no server-side meaning. When it runs out, `subscribe` makes one best-effort cancel before raising. A cancel only takes effect on a request that has not started running: a generation already in flight at the partner completes and is charged whether or not anyone collects it. Use `submit` when the request should outlive the caller.

### Collect from another process

Store the `request_id` next to the model ID. Both are needed to rebuild a handle, and no call is made until you use it.

<CodeGroup>
  ```python Python theme={null}
  handle = client.models.handle("bfl/flux-2-pro", request_id)
  result = handle.get()
  ```

  ```typescript TypeScript theme={null}
  const handle = comfy.models.handle<FluxResult>("bfl/flux-2-pro", requestId);
  const result = await handle.get();
  ```
</CodeGroup>

### Check status or cancel

`status()` is one poll and returns the current state. `cancel()` asks the server to stop a request that has not finished. It is a request, not a guarantee: a run already on the wire at the partner may complete anyway, and the next `status()` is what is true.

<CodeGroup>
  ```python Python theme={null}
  update = handle.status()
  print(update.status, update.queue_position, update.error_type)

  handle.cancel()
  ```

  ```typescript TypeScript theme={null}
  const update = await handle.status();
  console.log(update.status, update.queuePosition, update.errorType);

  await handle.cancel();
  ```
</CodeGroup>

### Async Python

`AsyncComfy` mirrors every name, argument and argument order. There is no `submit_async`, for the same reason there is no `run_async`.

```python theme={null}
from comfy_sdk import AsyncComfy

async with AsyncComfy() as client:
    handle = await client.models.submit(
        "bfl/flux-2-pro",
        {"prompt": "a red teapot on a windowsill, morning light"},
    )
    async for update in handle.iter_events():
        print(update.status, update.queue_position)
    result = await handle.get()
```

### Errors the SDKs raise

A request that finished without succeeding is reported as `COMPLETED` with an `error_type`. `get()` and `subscribe()` turn that into the typed Router error for the bucket: the classes in `comfy_sdk.router_exceptions` in Python, and `routerErrors.*` in TypeScript. The event iterator does not raise for that case, because it is a view of the queue's progress: a completion carrying an `error_type` is yielded as the last observation, and `get()` is what collects. A `403` `not_enabled` on submit arrives as `NotEnabled` and is terminal, so the SDKs do not retry it.

## 응답 형태

**제출, `201`.** 이 시점에서 `status`는 항상 `IN_QUEUE`입니다. 세 개의 URL은 절대 경로이며, 제출할 때와 동일한 키로 인증됩니다.

```json theme={null}
{
  "request_id": "6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21",
  "status": "IN_QUEUE",
  "queue_position": 3,
  "status_url": "https://api.comfy.org/v2/models/bfl/flux-2-pro/requests/6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21/status",
  "response_url": "https://api.comfy.org/v2/models/bfl/flux-2-pro/requests/6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21",
  "cancel_url": "https://api.comfy.org/v2/models/bfl/flux-2-pro/requests/6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21/cancel"
}
```

`request_id`는 제출 시의 `X-Comfy-Request-Id` 헤더 값이기도 합니다. 모델 ID를 함께 보관하세요. 요청은 두 값으로 지정됩니다.

**상태, `200`.** 동일한 형태에 현재 상태가 담겨 있습니다. `queue_position`은 내 요청보다 앞에 있는 요청 수를 세며, 실행이 맨 앞에 도달하면 `0`이 됩니다. 이 응답의 `Retry-After`는 다시 폴링할 만한 시점에 대한 Router의 추정치입니다. 이는 힌트일 뿐 상한이 아니며, 실행 대기열 뒤쪽의 요청은 이미 실행 중인 요청보다 더 오래 기다리라는 안내를 받습니다. 더 빠르게 폴링해도 더 일찍 알 수 있는 것은 없고 자신의 rate-limit 한도만 소모됩니다.

```json theme={null}
{
  "request_id": "6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21",
  "status": "IN_PROGRESS",
  "queue_position": 0,
  "status_url": "...",
  "response_url": "...",
  "cancel_url": "..."
}
```

성공하지 못하고 종료된 요청은 `error_type`을 동반한 `COMPLETED`이며, 결과 조회가 `X-Comfy-Error-Type`에 넣는 것과 동일한 포괄적 분류를 담습니다. 이 필드는 성공 시 `null`이 아니라 아예 존재하지 않습니다.

```json theme={null}
{
  "request_id": "6f1a1a6e-6a53-4a5f-9d3a-2b3b0a1f9c21",
  "status": "COMPLETED",
  "error_type": "content_policy_violation",
  "status_url": "...",
  "response_url": "...",
  "cancel_url": "..."
}
```

**결과.** `200`은 모델 자체의 네이티브 출력을 담으며, 동일한 모델과 입력에 대해 동기 경로가 반환하는 것과 바이트 단위로 동일하고, 공급자 자신의 `Content-Type`을 따릅니다. 요청이 아직 끝나지 않았다면 조회는 위의 상태 본문과 함께 `202`로 응답하므로, 결과 URL만 폴링하는 클라이언트는 하나의 타입만 파싱합니다. 실패한 요청은 `X-Comfy-Error-Type`이 설정된 오류 응답으로 돌아오며, 동기 경로와 동일한 분류를 사용합니다.

**취소.** `202`와 `CANCELLATION_REQUESTED`는 요청이 접수되었음을 의미할 뿐, 실행이 중단되었음을 뜻하지는 않습니다. 파트너 측에서 이미 전송 중인 실행은 그대로 완료될 수 있으며, 완료된 파트너 생성은 아무도 수집하지 않더라도 과금됩니다. 이후에 상태를 읽어 보세요. 취소가 적용된 경우 `error_type: cancelled`와 함께 `COMPLETED`로 표시됩니다. 실행되기 전에 만료된 요청은 같은 방식으로 `queue_timeout`을 표시합니다. 이미 완료된 요청은 `ALREADY_COMPLETED`와 함께 `409`로 응답합니다.

## 멱등성 및 과금

* **동기 라우트와 동일한 과금.** 과금은 공급자가 Comfy에 청구할 때 발생합니다. 실행 대기열에서 대기한 시간은 과금되지 않습니다.
* **제출당 하나의 `Idempotency-Key`.** SDK는 `submit` 호출마다 새로운 키를 생성하므로, 같은 입력을 두 번 의도적으로 제출하면 두 개의 요청이 됩니다. 같은 키로 같은 호출을 재시도해도 두 번째 실행이 대기열에 추가되지 않고, 원본 핸들을 `Idempotent-Replayed: true`와 함께 반환합니다. 응답 유실로 `request_id`를 잃을 수 있는 경우에는 직접 키를 전달하세요. [Headers](/ko/development/comfy-router/headers)를 참고하세요.
* **결과는 만료됩니다.** 완료된 요청은 완료 후 24시간 동안 보관됩니다. 그 이후에는 상태 및 결과 조회가 `410`을 반환하고 결과는 사라집니다. 신속히 수집하고 출력에 포함된 자산 URL을 다운로드하세요.
* **폴링도 요청입니다.** 상태 및 결과 조회는 [호출자별 요청 속도 제한](/ko/development/comfy-router/limitations#요청은-호출자별로-속도-제한됨)에 포함됩니다. 고정된 짧은 주기로 폴링하는 대신 `Retry-After`를 준수하세요.

## 오류

| 상태    | `X-Comfy-Error-Type`         | 의미                                                                                                                                                          |
| ----- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `402` | `insufficient_credits`       | 워크스페이스가 해당 실행에 자금을 지원할 수 없습니다. 아무것도 대기 중이거나 청구되지 않으며, 자금이 확보되면 동일한 `Idempotency-Key`를 다시 보낼 수 있습니다.                                                         |
| `403` | `not_enabled`                | 이 호출자는 실행 대기열을 사용할 수 없습니다. 뒤에 워크스페이스가 없는 키(워크스페이스 도입 이전의 레거시 키), 자체 키 사용(bring-your-own-key) 요청, 또는 실행 대기열이 실행할 수 없는 모델입니다. 해당 요청에 대해서는 종료 상태이며, 재시도하지 마세요. |
| `404` | `model_not_found`            | `{provider}/{model}` ID가 어떤 Router 모델로도 확인되지 않습니다.                                                                                                          |
| `404` | `request_not_found`          | 이 호출자와 모델에 대해 해당 ID를 가진 요청이 존재하지 않습니다.                                                                                                                      |
| `409` | `concurrency_limit_exceeded` | 동일한 `Idempotency-Key`가 아직 수락 처리 중입니다. `Retry-After`만큼 기다린 후 동일한 키를 다시 보내 원본 핸들을 받으세요.                                                                       |
| `409` | `invalid_input`              | `Idempotency-Key`가 다른 요청에 대해 점유되어 있습니다. 이 요청은 새 키로 보내세요.                                                                                                    |
| `409` | 본문에 `ALREADY_COMPLETED`      | 취소 경로에만 해당: 요청이 이미 완료되어 취소할 것이 없었습니다.                                                                                                                       |
| `410` |                              | 요청이 존재했지만 보존 기간이 지났습니다. 완료된 후 24시간이 지난 경우입니다. 해당 ID에 대해 영구적입니다.                                                                                             |
| `422` | `invalid_input`              | 모델이 입력을 거부했습니다. 본문에는 동기 경로에서와 정확히 동일하게 필드별 세부 정보가 포함됩니다.                                                                                                    |

모든 오류 응답에는 `X-Comfy-Request-Id`가 포함됩니다. 고객 지원에 문의할 때 이 값을 알려주세요.

## 다음

<CardGroup cols={2}>
  <Card title="빠른 시작" icon="rocket" href="/ko/development/comfy-router/quickstart">
    동일한 모델을 위한 동기 호출로, 아무것도 없는 상태에서 이미지 한 장까지.
  </Card>

  <Card title="모델" icon="grid" href="/ko/development/comfy-router/models">
    모든 모델 페이지에는 해당 모델과 본문에 맞는 대기 중 요청 스니펫이 있습니다.
  </Card>

  <Card title="헤더" icon="list" href="/ko/development/comfy-router/headers">
    인증, 멱등성, 요청 ID, 오류 버킷, 재시도 간격.
  </Card>

  <Card title="API 레퍼런스" icon="book" href="/ko/development/comfy-router/reference#엔드포인트">
    네 가지 실행 대기열 경로를 필드별로 설명합니다.
  </Card>
</CardGroup>
