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

# Async Tasks

> Unified async task API for video generation, image generation, music generation and other long-running tasks.

<Callout icon="lightbulb" color="#4885FF" iconType="regular">
  The unified async task API provides a standardized submit → poll → retrieve workflow, abstracting away provider-specific differences.
</Callout>

## Overview

AI tasks such as video generation, image generation, and music generation can take anywhere from tens of seconds to several minutes. To ensure a good user experience and system stability, these tasks are handled through an **async task API**:

1. **Submit a task** — Send a generation request and immediately receive a task ID
2. **Poll for status** — Use the task ID to check progress, or receive a callback notification
3. **Retrieve results** — Once complete, get the generated resource URLs from the response

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant API as ePhone AI
    Client->>API: POST /v1/task/submit
    API-->>Client: Return {id, status: "queued"}
    loop Poll (or wait for callback)
        Client->>API: GET /v1/task/{id}
        API-->>Client: Return {status: "in_progress"}
    end
    API-->>Client: Return {status: "completed", outputs: [...]}
```

***

## Submit a Task

<div className="mt-4">
  `POST /v1/task/submit`
</div>

### Request Parameters

| Field          | Type   | Required | Description                          |
| -------------- | ------ | -------- | ------------------------------------ |
| `model`        | string | Yes      | Model name                           |
| `input`        | object | Yes      | Model-specific generation parameters |
| `callback_url` | string | No       | Webhook URL for task status updates  |

### Request Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.ephone.ai/v1/task/submit \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "kling-v3/text-to-video",
      "input": {
        "prompt": "A golden retriever running under cherry blossom trees, slow motion, cinematic",
        "duration": "5",
        "aspect_ratio": "16:9"
      },
      "callback_url": "https://your-server.com/webhook/task"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.ephone.ai/v1/task/submit",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json",
      },
      json={
          "model": "kling-v3/text-to-video",
          "input": {
              "prompt": "A golden retriever running under cherry blossom trees, slow motion, cinematic",
              "duration": "5",
              "aspect_ratio": "16:9",
          },
          "callback_url": "https://your-server.com/webhook/task",
      },
  )

  data = response.json()
  print(data)
  # {"id": "task_abc123", "status": "queued", "created_at": 1711234567}
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.ephone.ai/v1/task/submit", {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "kling-v3/text-to-video",
      input: {
        prompt: "A golden retriever running under cherry blossom trees, slow motion, cinematic",
        duration: "5",
        aspect_ratio: "16:9",
      },
      callback_url: "https://your-server.com/webhook/task",
    }),
  });

  const data = await response.json();
  console.log(data);
  // {"id": "task_abc123", "status": "queued", "created_at": 1711234567}
  ```
</CodeGroup>

### Response Example

```json theme={null}
{
  "id": "task_abc123",
  "status": "queued",
  "created_at": 1711234567
}
```

***

## Query a Task

<div className="mt-4">
  `GET /v1/task/{task_id}`
</div>

### Request Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.ephone.ai/v1/task/task_abc123 \
    -H "Authorization: Bearer $API_KEY"
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.ephone.ai/v1/task/task_abc123",
      headers={"Authorization": "Bearer YOUR_API_KEY"},
  )

  data = response.json()
  print(data["status"])   # "queued" | "in_progress" | "completed" | "failed"
  print(data["outputs"])  # Resource URLs when completed
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.ephone.ai/v1/task/task_abc123", {
    headers: { Authorization: "Bearer YOUR_API_KEY" },
  });

  const data = await response.json();
  console.log(data.status);   // "queued" | "in_progress" | "completed" | "failed"
  console.log(data.outputs);  // Resource URLs when completed
  ```
</CodeGroup>

### Response Examples

**Task in progress:**

```json theme={null}
{
  "id": "task_abc123",
  "status": "in_progress",
  "created_at": 1711234567
}
```

**Task completed (token-based billing):**

```json theme={null}
{
  "id": "task_abc123",
  "status": "completed",
  "created_at": 1711234567,
  "completed_at": 1711234620,
  "outputs": [
    "https://cdn.example.com/video/result.mp4"
  ],
  "usage": {
    "type": "tokens",
    "input_tokens": 14,
    "input_token_details": {
      "audio_tokens": 14
    },
    "output_tokens": 45,
    "total_tokens": 59
  }
}
```

**Task completed (duration-based billing):**

```json theme={null}
{
  "id": "task_xyz789",
  "status": "completed",
  "created_at": 1711234567,
  "completed_at": 1711234620,
  "outputs": [
    "https://cdn.example.com/video/result.mp4"
  ],
  "usage": {
    "type": "duration",
    "seconds": 27
  }
}
```

**Task failed:**

```json theme={null}
{
  "id": "task_abc123",
  "status": "failed",
  "created_at": 1711234567,
  "completed_at": 1711234590,
  "error": "Content policy violation detected"
}
```

***

## Task Statuses

| Status        | Description                                    |
| ------------- | ---------------------------------------------- |
| `queued`      | Task submitted, waiting to be processed        |
| `in_progress` | Task is currently being generated              |
| `completed`   | Task finished — results available in `outputs` |
| `failed`      | Task failed — reason available in `error`      |

***

## Callback Notifications

When you include a `callback_url` in your submit request, the system will send a `POST` request to that URL whenever the task status changes. The callback body uses the same format as the query response:

```json theme={null}
{
  "id": "task_abc123",
  "status": "completed",
  "created_at": 1711234567,
  "completed_at": 1711234620,
  "outputs": [
    "https://cdn.example.com/video/result.mp4"
  ],
  "usage": {
    "type": "tokens",
    "input_tokens": 14,
    "output_tokens": 45,
    "total_tokens": 59
  }
}
```

<Warning>
  1. The callback URL must be publicly accessible over HTTPS
  2. Callback requests have a 10-second timeout
  3. Callback URLs cannot point to `localhost`, `127.0.0.1`, or the platform's own domain
  4. Even with callbacks enabled, implement polling as a fallback to avoid missing notifications due to network issues
</Warning>

***

## Complete Polling Example

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

  API_KEY = "YOUR_API_KEY"
  BASE_URL = "https://api.ephone.ai/v1"

  # 1. Submit task
  submit_resp = requests.post(
      f"{BASE_URL}/task/submit",
      headers={
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      },
      json={
          "model": "kling-v3/text-to-video",
          "input": {
              "prompt": "A golden retriever running under cherry blossom trees",
              "duration": "5",
              "aspect_ratio": "16:9",
          },
      },
  )

  task = submit_resp.json()
  task_id = task["id"]
  print(f"Task submitted: {task_id}")

  # 2. Poll until complete
  while True:
      resp = requests.get(
          f"{BASE_URL}/task/{task_id}",
          headers={"Authorization": f"Bearer {API_KEY}"},
      )
      result = resp.json()
      status = result["status"]
      print(f"Status: {status}")

      if status == "completed":
          print("Results:", result["outputs"])
          break
      elif status == "failed":
          print("Failed:", result.get("error", "Unknown error"))
          break

      time.sleep(5)
  ```

  ```javascript Node.js theme={null}
  const API_KEY = "YOUR_API_KEY";
  const BASE_URL = "https://api.ephone.ai/v1";

  // 1. Submit task
  const submitResp = await fetch(`${BASE_URL}/task/submit`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "kling-v3/text-to-video",
      input: {
        prompt: "A golden retriever running under cherry blossom trees",
        duration: "5",
        aspect_ratio: "16:9",
      },
    }),
  });

  const task = await submitResp.json();
  const taskId = task.id;
  console.log(`Task submitted: ${taskId}`);

  // 2. Poll until complete
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

  while (true) {
    const resp = await fetch(`${BASE_URL}/task/${taskId}`, {
      headers: { Authorization: `Bearer ${API_KEY}` },
    });
    const result = await resp.json();
    console.log(`Status: ${result.status}`);

    if (result.status === "completed") {
      console.log("Results:", result.outputs);
      break;
    } else if (result.status === "failed") {
      console.log("Failed:", result.error);
      break;
    }

    await sleep(5000);
  }
  ```
</CodeGroup>

***

## Response Fields

| Field          | Type      | Description                                                                                                                       |
| -------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | string    | Unique task identifier                                                                                                            |
| `status`       | string    | Task status: `queued` / `in_progress` / `completed` / `failed`                                                                    |
| `created_at`   | integer   | Task creation time (Unix timestamp in seconds)                                                                                    |
| `completed_at` | integer   | Task completion time (Unix timestamp in seconds), only present when completed or failed                                           |
| `outputs`      | string\[] | List of result URLs (video/image/audio), only present when `completed`                                                            |
| `usage`        | object    | Task usage information; see [Usage Information](#usage-information) section. Only present in terminal states for supported models |
| `error`        | string    | Error message, only present when `failed`                                                                                         |

***

## Usage Information

After a task reaches a terminal state (`completed` / `failed`), some models include a `usage` field in the response that reports the actual resources consumed. The `usage` field is a **tagged union**: the `type` field distinguishes the metering unit.

### type=tokens (token-based billing)

Used by Doubao / Vidu / Ali / Kling text-based / Gemini and other token-billed tasks:

```json theme={null}
{
  "type": "tokens",
  "input_tokens": 14,
  "input_token_details": {
    "audio_tokens": 14
  },
  "output_tokens": 45,
  "total_tokens": 59
}
```

| Field                               | Type    | Description                                                                                                                                     |
| ----------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`                              | string  | Always `"tokens"`                                                                                                                               |
| `input_tokens`                      | integer | Total input tokens                                                                                                                              |
| `input_token_details`               | object  | Input token breakdown by modality (optional); zero-valued sub-fields are omitted, and the entire object is omitted when all sub-fields are zero |
| `input_token_details.text_tokens`   | integer | Text input tokens                                                                                                                               |
| `input_token_details.audio_tokens`  | integer | Audio input tokens                                                                                                                              |
| `input_token_details.image_tokens`  | integer | Image input tokens                                                                                                                              |
| `input_token_details.cached_tokens` | integer | Tokens served from prompt cache (compatible with OpenAI prompt cache)                                                                           |
| `output_tokens`                     | integer | Total output tokens                                                                                                                             |
| `output_token_details`              | object  | Output token breakdown by modality (optional); same omission rules as `input_token_details`                                                     |
| `total_tokens`                      | integer | `input_tokens + output_tokens` total                                                                                                            |

### type=duration (duration-based billing)

Used by Sora / Xai-video and other video generation tasks billed by output duration:

```json theme={null}
{
  "type": "duration",
  "seconds": 27
}
```

| Field     | Type   | Description                                      |
| --------- | ------ | ------------------------------------------------ |
| `type`    | string | Always `"duration"`                              |
| `seconds` | number | Task usage duration in seconds (decimal allowed) |

<Note>
  * Tasks billed strictly per-call (e.g., Flux / Replicate / Midjourney) do not return a `usage` field
  * The `usage` field is not returned while the task is `queued` or `in_progress`
  * Historical tasks and tasks where the upstream did not report usage data also omit this field
  * Sub-fields with a zero value (e.g., `text_tokens: 0`) are omitted
</Note>

***

## Notes

<Warning>
  1. Authentication uses standard `Authorization: Bearer <API_KEY>` headers
  2. The `input` parameters vary by model — click on the model in the [Models page](https://platform.ephone.ai/models) to view its documentation
  3. Recommended polling interval is 3–10 seconds to avoid excessive requests
  4. Resource URLs in task results may have expiration limits — download and save promptly
</Warning>

## Related Links

<Columns cols={2}>
  <Card title="Video Models" icon="video" href="/docs/en/guides/video" arrow="true" cta="Learn more">
    Provider-specific video generation API documentation
  </Card>

  <Card title="Image Models" icon="image" href="/docs/en/guides/image" arrow="true" cta="Learn more">
    Image generation API documentation
  </Card>
</Columns>
