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

# Async Tasks

> AIHubMix async task API: pass async for images, video tasks, and LLM interruption recovery. Check status, download results, and get webhooks via /ai/v1/tasks.

Requests such as video generation and batch image generation usually take longer than a single HTTP connection can reasonably wait. For long text generation, any response already produced is lost once the client disconnects.

**Async Tasks** bring these three scenarios into a single task object: image and video requests create a task through the generation endpoints and return a `task_id` immediately, while an interrupted LLM request is completed by the platform and its final response is saved. All three share the same task statuses, query endpoints, and result download flow.

<Note>
  Use the same API Key that created the task to query it and download its results. Tasks are isolated per API Key. Two keys under the same account cannot read each other's tasks.
</Note>

<Card title="Enable async tasks in the console" icon="list-check" href="https://console.aihubmix.com/support" horizontal>
  Enable async tasks for your account before creating async image or video tasks. If the entry is not visible in the console yet, contact AIHubMix technical support.
</Card>

<Warning>
  When async tasks are not enabled, media task creation requests return `403 async_not_enabled`. LLM requests do not fail because of this, but the final response cannot be retrieved after a client interruption.
</Warning>

***

<h2 id="quickstart">
  Quickstart
</h2>

The full flow for async image and video tasks has three steps:

```text theme={null}
1. Submit the task -> get a task_id
2. Query the status -> wait for the task to finish
3. Get the result -> download the file or read the response content
```

<CodeGroup>
  ```shell curl theme={null}
  # Step 1: submit an async video task
  curl -X POST https://aihubmix.com/ai/v1/videos \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "wan2.6-t2v",
      "prompt": "A cat playing jazz on a piano, warm lighting, cinematic shot",
      "seconds": "5",
      "size": "1280x720"
    }'

  # Step 2: poll every 15 seconds until the task is completed, failed, or cancelled
  curl https://aihubmix.com/ai/v1/tasks/{task_id} \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY"

  # Step 3: download a single result
  curl https://aihubmix.com/ai/v1/tasks/{task_id}/content \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
    --output result.mp4
  ```

  ```json Creation response theme={null}
  {
    "id": "task_01K0...",
    "object": "video",
    "model": "wan2.6-t2v",
    "status": "in_progress",
    "output": [],
    "error": null,
    "created_at": 1784707200,
    "completed_at": null,
    "expires_at": null
  }
  ```
</CodeGroup>

***

<h2 id="sync-vs-async">
  Synchronous Calls vs. Async Tasks
</h2>

| Request type        | Default return mode                        | Async mode                                                                                              |
| ------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| Image generation    | Returns the generated result synchronously | Pass `async: true` in the request body to return a `task_id` immediately                                |
| Video generation    | Always asynchronous                        | Returns a `task_id` after creation; results are retrieved through the task endpoints                    |
| LLM text generation | Synchronous or streaming response          | When the client is interrupted and the conditions are met, the final response is saved as an `llm` task |

A synchronous call returns the result within a single HTTP response, and the result is unavailable once the connection drops. An async task stores the result on the platform, and the `task_id` can be used with the same API Key to query and download it again before the result expires. This suits long-running generation requests, and long text output that needs to be retrieved after an interruption.

***

<h2 id="api-overview">
  API Overview
</h2>

| Operation                  | Method | Path                                         | Description                               |
| -------------------------- | ------ | -------------------------------------------- | ----------------------------------------- |
| Create an async image task | POST   | `/ai/v1/images/generations`                  | Add `async: true` to the request body     |
| Create an async video task | POST   | `/ai/v1/videos`                              | Video tasks are asynchronous by default   |
| List tasks                 | GET    | `/ai/v1/tasks`                               | Find tasks created by the current API Key |
| Get task details           | GET    | `/ai/v1/tasks/{task_id}`                     | Query the unified task status and output  |
| Get a single result        | GET    | `/ai/v1/tasks/{task_id}/content`             | For single-output or LLM tasks            |
| Get a specific result      | GET    | `/ai/v1/tasks/{task_id}/content/{result_id}` | For multi-output tasks                    |

Base URL: `https://aihubmix.com`, authenticated with a Bearer token:

```bash theme={null}
Authorization: Bearer $AIHUBMIX_API_KEY
```

<Note>
  `/ai/v1/tasks` is a read-only unified query entry point and does not provide `POST /ai/v1/tasks`. Images and videos are created through their respective generation endpoints. Requests that meet the [LLM interruption recovery](#llm-interruption-recovery) conditions are automatically recorded as `llm` tasks after the client is interrupted.
</Note>

***

<h2 id="supported-models">
  Supported Models
</h2>

Async task support is defined per task type, and no extra parameter is needed when calling.

<h3 id="supported-models-image">
  Async Images
</h3>

| Model            |
| ---------------- |
| `qwen-image-2.0` |

<h3 id="supported-models-video">
  Async Videos
</h3>

| Model        |
| ------------ |
| `wan2.6-t2v` |

<h3 id="supported-models-llm">
  LLM Interruption Recovery
</h3>

| Model            |
| ---------------- |
| `gpt-5.5-pro`    |
| `claude-fable-5` |

The supported range keeps expanding, and this table is updated accordingly.

***

<h2 id="create-async-task">
  How to Create an Async Task
</h2>

<h3 id="create-async-image">
  Async Images
</h3>

The image endpoint returns synchronously by default. Setting `async` to `true` makes the endpoint return a task object immediately while generation continues in the background.

```bash theme={null}
curl -X POST https://aihubmix.com/ai/v1/images/generations \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-image-2.0",
    "prompt": "A flower shop with delicate windows, warm sunlight streaming in",
    "n": 2,
    "size": "1024x1024",
    "async": true
  }'
```

`async` must be a boolean. When it is omitted or set to `false`, the image endpoint keeps its synchronous behavior.

<h3 id="create-async-video">
  Async Videos
</h3>

The video endpoint is always asynchronous. A successful creation returns the `pending` or `in_progress` status, and switching to synchronous waiting through `Prefer: wait` is not supported.

```bash theme={null}
curl -X POST https://aihubmix.com/ai/v1/videos \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan2.6-t2v",
    "prompt": "Ocean waves crashing on rocky cliffs at sunset",
    "seconds": "5",
    "size": "1280x720"
  }'
```

<h3 id="common-parameters">
  Common Parameters
</h3>

`model`, `prompt`, `n`, `seconds`, and `size` in the examples are common model parameters. The fields and values each model supports are defined by that model's API documentation; for video models, see the [video generation docs](/en/api/Video-Gen). The table below only covers the parameters shared by all async tasks.

| Parameter               | Type      | Required                        | Description                                                            |
| ----------------------- | --------- | ------------------------------- | ---------------------------------------------------------------------- |
| `async`                 | boolean   | Images: yes; videos: not needed | Set to `true` for asynchronous execution on the image endpoint         |
| `webhook_url`           | string    | No                              | HTTPS callback URL for the current task, up to 512 characters          |
| `webhook_events_filter` | string\[] | No                              | Final statuses to push, chosen from `completed`, `failed`, `cancelled` |

<Note>
  Image tasks can only use webhooks when `async: true`. When `webhook_events_filter` is omitted, the platform pushes all three final statuses: `completed`, `failed`, and `cancelled`. When it is provided, it must be used together with `webhook_url`, and it cannot be empty or contain duplicates.
</Note>

***

<h2 id="llm-interruption-recovery">
  How LLM Interruption Recovery Works
</h2>

LLM interruption recovery is used to retrieve the final response after the client disconnects. It reuses the existing way of making LLM requests, keeps streaming behavior and response formats unchanged, requires no additional creation endpoint, and does not return a `task_id` in advance.

<h3 id="recovery-conditions">
  Conditions
</h3>

All of the following conditions must be met at the same time:

| Condition                                | Description                                                                                       |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Async tasks are enabled for the account  | Enable it for the current account in the AIHubMix console                                         |
| The model supports interruption recovery | See [LLM Interruption Recovery](#supported-models-llm); no extra parameter is needed when calling |
| A supported LLM endpoint is called       | The request hits one of the text generation endpoints listed below                                |
| The client is interrupted                | The client cancels, the network drops, or the caller cancels the request                          |

Supported endpoints:

| Endpoint                                           | Description                                          |
| -------------------------------------------------- | ---------------------------------------------------- |
| `POST /v1/chat/completions`                        | OpenAI Chat Completions, streaming and non-streaming |
| `POST /v1/messages`                                | Anthropic Messages, streaming and non-streaming      |
| `POST /v1/responses`                               | OpenAI Responses API                                 |
| Gemini `generateContent` / `streamGenerateContent` | Gemini native text generation endpoints              |

<Note>
  No extra field is needed when calling. For the supported range, see [LLM Interruption Recovery](#supported-models-llm). For models not listed in that table, run one low-cost request to verify interruption recovery before going live; the verification request is still billed normally. If any condition is not met, the request still runs normally, and no `llm` task is created after a client interruption.
</Note>

<h3 id="recovery-flow">
  Execution Flow After an Interruption
</h3>

```text theme={null}
1. The client sends an LLM request normally
2. The client disconnects or cancels before the response completes
3. AIHubMix continues processing the request, and the request is still billed normally
4. The final JSON or SSE response is saved as a task of type llm
5. Use the original API Key to list tasks and read the saved response
```

LLM requests that complete normally and are returned to the client successfully do not create a task and do not appear in the task list. An interrupted request appears in the list after its final response has been saved, so it may be temporarily unavailable while processing is still under way.

<h3 id="locate-interrupted-request">
  Locating the Matching Interrupted Request
</h3>

LLM responses include the `X-Aihubmix-Request-Id` response header. The client should save this value as soon as the response headers arrive. After an interruption, the request ID can be used to find the matching task in the async task list in the AIHubMix console.

The public task API currently does not support filtering by request ID. If the request ID was not saved, the only option is to use the same API Key that created the request and search by model and creation time:

```bash theme={null}
# Query recent interrupted LLM tasks
curl "https://aihubmix.com/ai/v1/tasks?object=llm&model={model}&order=desc&limit=20" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"

# After finding the task_id, get the details and the original response
curl https://aihubmix.com/ai/v1/tasks/{task_id} \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"

curl https://aihubmix.com/ai/v1/tasks/{task_id}/content \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

<Warning>
  When one API Key sends several concurrent requests to the same model, model and creation time alone cannot guarantee an exact match. For reliable recovery, save `X-Aihubmix-Request-Id` and look the task up in the console. If the response headers were never received, avoid treating the most recent task in the list as the one from this request.
</Warning>

<Warning>
  LLM interruption recovery tasks currently do not send webhooks, so query the results through the task list. A client interruption does not stop the platform from continuing to process the request, and the call is still billed under the rules of the original LLM endpoint.
</Warning>

***

<h2 id="task-object">
  Task Object and Statuses
</h2>

All tasks use a unified response structure:

```json theme={null}
{
  "id": "task_01K0ABCDEF",
  "object": "video",
  "model": "wan2.6-t2v",
  "status": "completed",
  "output": [
    {
      "index": 0,
      "result_id": "result_01K0XYZ",
      "type": "file",
      "content_type": "video/mp4",
      "content_url": "https://aihubmix.com/ai/v1/tasks/task_01K0ABCDEF/content"
    }
  ],
  "error": null,
  "created_at": 1784707200,
  "completed_at": 1784707320,
  "expires_at": 1784709120
}
```

| Field          | Type         | Description                                                            |
| -------------- | ------------ | ---------------------------------------------------------------------- |
| `id`           | string       | Platform task ID, used as the `task_id` in later requests              |
| `object`       | string       | Task type: `llm`, `image`, or `video`                                  |
| `model`        | string       | Model used when the task was created                                   |
| `status`       | string       | Unified task status                                                    |
| `output`       | array        | Available results; an empty array when the task has produced no result |
| `error`        | object/null  | Failure details, usually containing `code` and `message`               |
| `created_at`   | integer      | Creation time, Unix seconds                                            |
| `completed_at` | integer/null | Time when the task completed, failed, or was cancelled, Unix seconds   |
| `expires_at`   | integer/null | Expiration time of the earliest result, Unix seconds                   |

Result fields inside `output`:

| Field          | Description                                                               |
| -------------- | ------------------------------------------------------------------------- |
| `index`        | Position of the result within the current task, starting from 0           |
| `result_id`    | Result ID, used to download a specific result of a multi-output task      |
| `type`         | Result type: `file` for files, `response` for LLM responses               |
| `content_type` | File type (MIME) of the result, such as `video/mp4` or `application/json` |
| `content_url`  | Result download URL; access requires the API Key that created the task    |
| `b64_json`     | Base64-encoded result that some image models may return directly          |
| `truncated`    | Whether the LLM response was truncated because of a size limit            |

<h3 id="task-status">
  Status Reference
</h3>

| Status        | Final | Description                                                   |
| ------------- | ----- | ------------------------------------------------------------- |
| `pending`     | No    | The platform has accepted the task and it is waiting to start |
| `in_progress` | No    | The task is running                                           |
| `completed`   | Yes   | The task is complete and results are available in `output`    |
| `failed`      | Yes   | The task failed; see `error` for the reason                   |
| `cancelled`   | Yes   | The task was cancelled                                        |

Poll every **15 seconds** until the status becomes `completed`, `failed`, or `cancelled`.

<Note>
  A `failed` or `cancelled` task may still contain results that were already generated. When checking whether results exist, also check whether `output` is empty in addition to the status.
</Note>

***

<h2 id="query-tasks">
  How to Query Tasks
</h2>

<h3 id="query-task-detail">
  Get Task Details
</h3>

```bash theme={null}
curl https://aihubmix.com/ai/v1/tasks/{task_id} \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

This endpoint returns the latest task information at query time. Querying does not change the task; the task status is updated automatically by the platform.

<h3 id="query-task-list">
  List Tasks
</h3>

If the creation response is lost, or historical tasks need to be reviewed in bulk, the list endpoint can recover the `task_id`:

```bash theme={null}
curl "https://aihubmix.com/ai/v1/tasks?object=video&status=in_progress&limit=20&order=desc" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

| Parameter | Type    | Default | Description                                                  |
| --------- | ------- | ------- | ------------------------------------------------------------ |
| `object`  | string  | -       | Filter by type: `llm`, `image`, `video`                      |
| `status`  | string  | -       | Filter by unified task status                                |
| `model`   | string  | -       | Exact filter by model name                                   |
| `after`   | string  | -       | Pagination cursor, using `next_after` from the previous page |
| `limit`   | integer | `20`    | Items per page, from 1 to 100                                |
| `order`   | string  | `desc`  | `asc` or `desc`                                              |

Example response:

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "task_01K0ABCDEF",
      "object": "video",
      "model": "wan2.6-t2v",
      "status": "in_progress",
      "output": [],
      "error": null,
      "created_at": 1784707200,
      "completed_at": null,
      "expires_at": null
    }
  ],
  "has_more": true,
  "next_after": "task_01K0ABCDEF"
}
```

| Field        | Description                                                            |
| ------------ | ---------------------------------------------------------------------- |
| `object`     | Always `list`, indicating a list response                              |
| `data`       | Array of tasks on the current page                                     |
| `has_more`   | Whether another page is available                                      |
| `next_after` | Cursor for the next page; returned only when another page is available |

Request the next page:

```bash theme={null}
curl "https://aihubmix.com/ai/v1/tasks?limit=20&order=desc&after=task_01K0ABCDEF" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

***

<h2 id="get-task-results">
  How to Get Task Results
</h2>

<h3 id="single-artifact">
  Single-Output Tasks
</h3>

When `output` contains only one file, it can be accessed directly:

```bash theme={null}
curl https://aihubmix.com/ai/v1/tasks/{task_id}/content \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  --output result.bin
```

`output[0].content_url` can also be used directly. The `Content-Type` of the download response matches `output[0].content_type`.

<h3 id="multiple-artifacts">
  Multi-Output Tasks
</h3>

When `output` contains several files, the matching `result_id` must be specified:

```bash theme={null}
curl https://aihubmix.com/ai/v1/tasks/{task_id}/content/{result_id} \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  --output result.png
```

If `result_id` is not specified for a multi-output task, the endpoint returns `400 result_id_required`.

<h3 id="llm-response-task">
  LLM Response Tasks
</h3>

Once the [LLM interruption recovery](#llm-interruption-recovery) conditions are met and the response is saved, the task has `object` set to `llm` and the `output` item has `type` set to `response`. The content type can be:

* `application/json`: a regular JSON response
* `text/event-stream`: a saved SSE streaming response

```bash theme={null}
curl https://aihubmix.com/ai/v1/tasks/{task_id}/content \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY"
```

The truncation flag is in `output[0].truncated` of the task details. A value of `true` means the saved response was truncated because of a size limit. `GET /ai/v1/tasks/{task_id}/content` returns the raw JSON or SSE content without wrapping it in a `truncated` field, so query the task details before reading the content.

<Warning>
  Results can expire, and a download count limit may apply. Save them before `expires_at`. An expired result returns `410 artifact_expired`, and exceeding the download limit returns `429 too_many_downloads`.
</Warning>

***

<h2 id="webhooks">
  How to Use Webhooks
</h2>

Task-level webhooks can currently be submitted when creating an async task. To have AIHubMix notify you after a task finishes, pass `webhook_url` and the optional `webhook_events_filter` in the async image or video request body:

```bash theme={null}
curl -X POST https://aihubmix.com/ai/v1/videos \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan2.6-t2v",
    "prompt": "A tranquil Japanese garden at sunrise",
    "seconds": "5",
    "webhook_url": "https://example.com/webhooks/aihubmix",
    "webhook_events_filter": ["completed", "failed"]
  }'
```

The callback URL must use HTTPS and must not point to localhost, private networks, or other restricted addresses.

<h3 id="webhook-payload">
  Callback Request
</h3>

AIHubMix sends a `POST` request to the callback URL:

```json theme={null}
{
  "event_id": "evt_01K0ABCDEF",
  "event_type": "completed",
  "created_at": "2026-07-22T12:00:00Z",
  "data": {
    "task_id": "task_01K0ABCDEF",
    "status": "completed",
    "model": "wan2.6-t2v",
    "results": [
      {
        "url": "https://aihubmix.com/ai/v1/tasks/task_01K0ABCDEF/content"
      }
    ]
  }
}
```

| Field                | Description                                                                |
| -------------------- | -------------------------------------------------------------------------- |
| `event_id`           | Unique ID of this callback event, used to identify duplicate notifications |
| `event_type`         | Final task status: `completed`, `failed`, or `cancelled`                   |
| `created_at`         | Creation time of the callback event                                        |
| `data.task_id`       | Task ID, usable to query the task details                                  |
| `data.status`        | Current task status                                                        |
| `data.model`         | Model used when the task was created                                       |
| `data.results[].url` | Download URL of a generated result                                         |
| `data.error.code`    | Failure error code, present only in failure events                         |
| `data.error.message` | Failure reason, present only in failure events                             |

The URLs in `results` still require the API Key that created the task.

<h3 id="webhook-retry">
  Retries and Deduplication
</h3>

The platform attempts delivery at least once, so the same event may be sent more than once:

* HTTP `2xx` means the callback was received successfully.
* HTTP `5xx`, network errors, or timeouts trigger a retry.
* HTTP `3xx` and `4xx` are not retried.
* Up to 6 deliveries, with retry intervals of 1, 4, 16, 64, and 256 seconds.

The receiver should store `event_id`. When the same `event_id` arrives again, skip the business logic and return `2xx` directly.

<Warning>
  Task-level webhooks currently do not provide configurable standalone signing credentials. After receiving a notification, use the API Key that created the task to request `GET /ai/v1/tasks/{task_id}` and treat the query result as authoritative.
</Warning>

***

<h2 id="error-codes">
  Error Responses and Error Codes
</h2>

Error responses use a unified structure:

```json theme={null}
{
  "error": {
    "message": "Task not found.",
    "type": "invalid_request_error",
    "code": "task_not_found",
    "tid": "req_01K0..."
  }
}
```

| Field           | Description                                                    |
| --------------- | -------------------------------------------------------------- |
| `error.message` | Reason for the error                                           |
| `error.type`    | Error type                                                     |
| `error.code`    | Machine-readable error code                                    |
| `error.tid`     | Request trace ID; provide it when contacting technical support |

| HTTP status | Error code                      | Description                                                        |
| ----------- | ------------------------------- | ------------------------------------------------------------------ |
| 400         | `invalid_request`               | Incorrect parameter type or value                                  |
| 400         | `result_id_required`            | `result_id` not specified for a multi-output task                  |
| 400         | `webhook_invalid`               | Invalid webhook URL                                                |
| 400         | `webhook_events_filter_invalid` | Invalid webhook event list                                         |
| 401         | `authentication_failed`         | Missing or invalid API Key                                         |
| 403         | `async_not_enabled`             | Async tasks are not enabled for the account                        |
| 404         | `task_not_found`                | The task does not exist, or does not belong to the current API Key |
| 404         | `result_not_found`              | The result does not exist or is currently unavailable              |
| 410         | `artifact_expired`              | The result has expired                                             |
| 429         | `too_many_downloads`            | The result download limit has been exceeded                        |
| 503         | `async_unavailable`             | The async image service is temporarily unavailable; retry later    |

***

<h2 id="full-example">
  Full Example
</h2>

The full flow of creating a video task, polling its status, and downloading all results:

<CodeGroup>
  ```python Python theme={null}
  import base64
  import os
  import time

  import requests

  BASE_URL = "https://aihubmix.com"
  API_KEY = os.environ["AIHUBMIX_API_KEY"]
  HEADERS = {
      "Authorization": f"Bearer {API_KEY}",
      "Content-Type": "application/json",
  }

  # 1. Create the task
  response = requests.post(
      f"{BASE_URL}/ai/v1/videos",
      headers=HEADERS,
      json={
          "model": "wan2.6-t2v",
          "prompt": "A cat playing jazz on a piano",
          "seconds": "5",
          "size": "1280x720",
      },
      timeout=60,
  )
  response.raise_for_status()
  task = response.json()
  task_id = task["id"]

  # 2. Poll until the task is completed, failed, or cancelled
  while task["status"] not in {"completed", "failed", "cancelled"}:
      time.sleep(15)
      response = requests.get(
          f"{BASE_URL}/ai/v1/tasks/{task_id}",
          headers=HEADERS,
          timeout=30,
      )
      response.raise_for_status()
      task = response.json()
      print("status:", task["status"])

  # 3. Get the results
  if task["output"]:
      for index, item in enumerate(task["output"]):
          if encoded := item.get("b64_json"):
              with open(f"result-{index}.bin", "wb") as file:
                  file.write(base64.b64decode(encoded))
              continue
          result = requests.get(
              item["content_url"],
              headers=HEADERS,
              timeout=120,
          )
          result.raise_for_status()
          with open(f"result-{index}.bin", "wb") as file:
              file.write(result.content)
  elif task["status"] == "failed":
      raise RuntimeError(task.get("error"))
  ```

  ```typescript TypeScript theme={null}
  import { writeFile } from "node:fs/promises";

  const BASE_URL = "https://aihubmix.com";
  const HEADERS = {
    Authorization: `Bearer ${process.env.AIHUBMIX_API_KEY}`,
    "Content-Type": "application/json",
  };

  // 1. Create the task
  const created = await fetch(`${BASE_URL}/ai/v1/videos`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      model: "wan2.6-t2v",
      prompt: "A cat playing jazz on a piano",
      seconds: "5",
      size: "1280x720",
    }),
  });
  let task = await created.json();

  // 2. Poll until the task is completed, failed, or cancelled
  const finished = new Set(["completed", "failed", "cancelled"]);
  while (!finished.has(task.status)) {
    await new Promise((resolve) => setTimeout(resolve, 15_000));
    const polled = await fetch(`${BASE_URL}/ai/v1/tasks/${task.id}`, {
      headers: HEADERS,
    });
    task = await polled.json();
    console.log("status:", task.status);
  }

  // 3. Get the results
  if (task.output?.length) {
    for (const [index, item] of task.output.entries()) {
      if (item.b64_json) {
        await writeFile(`result-${index}.bin`, Buffer.from(item.b64_json, "base64"));
        continue;
      }
      const result = await fetch(item.content_url, { headers: HEADERS });
      await writeFile(`result-${index}.bin`, Buffer.from(await result.arrayBuffer()));
    }
  } else if (task.status === "failed") {
    throw new Error(JSON.stringify(task.error));
  }
  ```

  ```shell curl theme={null}
  # 1. Create the task and record the returned id
  curl -X POST https://aihubmix.com/ai/v1/videos \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "wan2.6-t2v",
      "prompt": "A cat playing jazz on a piano",
      "seconds": "5",
      "size": "1280x720"
    }'

  # 2. Query the status every 15 seconds
  curl https://aihubmix.com/ai/v1/tasks/task_01K0ABCDEF \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY"

  # 3. Download the result once the status becomes completed
  curl https://aihubmix.com/ai/v1/tasks/task_01K0ABCDEF/content \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
    --output result.mp4
  ```
</CodeGroup>

***

<h2 id="faq">
  FAQ
</h2>

**How often should the task status be queried?**

Poll every 15 seconds and avoid high-frequency polling. Even when using webhooks, keep low-frequency polling as a backup.

**How can a task be recovered after the creation response is lost?**

Use the same API Key that created the task to request `GET /ai/v1/tasks`, and narrow the range with `object`, `model`, and `status`.

**Why can another API Key under the same account not find the task?**

Tasks are isolated per API Key. Query, download, and list requests must all use the same key that created the task.

**Why is `output` not an empty array when the task failed?**

Some models may have produced deliverable results before the task failed or was cancelled overall. As long as `content_url` or `b64_json` is present in `output`, the result can be retrieved in the corresponding way.

**What should be done when a webhook is not received?**

Confirm that the callback URL is publicly reachable, uses HTTPS, and returns `2xx` within 10 seconds. The final status can always be queried through `GET /ai/v1/tasks/{task_id}`, whether or not webhooks are used.

**Does LLM interruption recovery require changes to existing code?**

No. The request method, streaming behavior, and response format stay the same. Save the `X-Aihubmix-Request-Id` response header so the matching task can be located precisely in the console after an interruption.

***

Last updated: 2026-07-28
