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

# Use the Modellix REST API for Media Generation

> Use the Modellix REST API to authenticate, upload media inputs, submit an asynchronous image, video, or audio task, poll its status, and retrieve output assets.

## Steps

<Steps>
  <Step title="Register Modellix">
    Log in to the [Modellix console](https://modellix.ai/console).
  </Step>

  <Step title="Get API Key">
    In the Modellix console, go to "[API Key](https://modellix.ai/console/api-key)" and create an API Key.

    <Note>The API Key is only displayed once after creation, so be sure to save it first.</Note>
  </Step>

  <Step title="Use the API">
    Find the model you want to use in **Model API**, or [Model Index](https://docs.modellix.ai/llms.txt), and call the API using your API Key. For example:

    ```bash theme={null}
    curl --request POST \
      --url https://api.modellix.ai/api/v1/alibaba/qwen-image-plus/async \
      --header 'Authorization: Bearer <your_api_key>' \
      --header 'Content-Type: application/json' \
      --data '
    {
      "prompt": "A cute cat playing in a garden on a sunny day"
    }
    '
    ```

    Since model calls are asynchronous tasks, after a successful call, you will first receive a `task_id` as shown below:

    ```json theme={null}
    {
      "code": 0,
      "message": "success",
      "data": {
        "status": "pending",
        "task_id": "task-abc123",
        "model_id": "qwen-image-plus",
        "get_result": {
          "method": "GET",
          "url": "https://api.modellix.ai/api/v1/tasks/task-abc123"
        }
      }
    }
    ```
  </Step>

  <Step title="Get the Result">
    You can query the task result later using the `task_id`. For example:

    ```bash theme={null}
    curl --request GET \
      --url https://api.modellix.ai/api/v1/tasks/{task_id} \
      --header 'Authorization: Bearer <your_api_key>'
    ```

    After a successful query, you will get the generated result. For example:

    ```json theme={null}
    {
      "code": 0,
      "message": "success",
      "data": {
        "status": "success",
        "task_id": "task-abc123",
        "model_id": "qwen-image-plus",
        "duration": 3500,
        "result": {
          "resources": [
            {
              "url": "https://cdn.example.com/images/abc123.png",
              "type": "image",
              "width": 1024,
              "height": 1024,
              "format": "png",
              "role": "primary"
            }
          ],
          "metadata": {
            "image_count": 1,
            "request_id": "req-123456"
          },
          "extensions": {
            "submit_time": "2024-01-01T10:00:00Z",
            "end_time": "2024-01-01T10:00:03Z"
          }
        }
      }
    }
    ```

    <Note>The model's generated results will all be placed in the `result` object.</Note>

    You can find the generated image URL in the `result`. For example:

    ```json theme={null}
    {
      "url": "https://cdn.example.com/images/abc123.png"
    }
    ```
  </Step>

  <Step title="Enjoy the Result">
    You have now successfully used the Modellix model API and obtained the generated result.

    <Warning>Generated results are only saved for 7 days, so please make sure to save them promptly.</Warning>
  </Step>
</Steps>

## Request logs

List your team's media request history with:

```bash theme={null}
curl -sS "https://api.modellix.ai/api/v1/logs?start_time=1700000000&end_time=1700086400&page=1&page_size=20" \
  -H "Authorization: Bearer ${API_KEY}"
```

`start_time` / `end_time` are UNIX seconds (span ≤ 30 days). Optional `mdlx_user_id` filters by the end-user id sent as `X-Mdlx-User-Id` on async inference. Full reference: [List media request logs](/api/get-logs).

For LLM request logs on `https://llm.modellix.ai`, see [LLM request logs](/llm/api/api#request-logs).

## User ID

Optional. Tag media async inference requests with your own end-user identifier so you can filter [request logs](#request-logs) later. Invalid values return `400`.

| Header | Rules |
| - | - |
| `X-Mdlx-User-Id` | Optional; length 8–128; ASCII letters, digits, `-`, and `_` only |

```bash theme={null}
curl --request POST \
  --url https://api.modellix.ai/api/v1/alibaba/qwen-image-plus/async \
  --header 'Authorization: Bearer <your_api_key>' \
  --header 'Content-Type: application/json' \
  --header 'X-Mdlx-User-Id: end_user_01' \
  --data '
{
  "prompt": "A cute cat playing in a garden on a sunny day"
}
'
```

The list response does **not** include a `mdlx_user_id` field; filter with the query parameter instead.

## Webhooks

Modellix supports webhooks to notify your application automatically when a media generation task is completed. Instead of polling the task status, you can configure a Webhook URL to receive the task results asynchronously.

### Triggering Webhooks

To enable webhooks for a task, include the `X-Webhook-URL` header when calling any prediction creation API:

```http theme={null}
X-Webhook-URL: https://example.com/webhook
```

Once the task reaches a terminal state, the system asynchronously sends a `POST` request to this URL. Terminal states include:

* `success`
* `failed`
* `canceled`

### Webhook URL Requirements

Your webhook endpoint must meet the following requirements:

* Must be a publicly accessible **HTTPS** address.
* Cannot be `localhost` or `127.0.0.1`.
* Cannot be a private IP address (e.g., `10.x.x.x`, `172.16.x.x` to `172.31.x.x`, `192.168.x.x`).
* Cannot contain username and password credentials in the URL.

<Tip>
  For local development and testing, you can use tools like [ngrok](https://ngrok.com/) or [cloudflared](https://github.com/cloudflare/cloudflared) to expose your local server via a public HTTPS URL.
</Tip>

### Callback Request

The system will initiate a `POST` request to your configured `X-Webhook-URL` with the following headers:

| Header | Description |
| :- | :- |
| `Content-Type` | Always `application/json` |
| `User-Agent` | Always `modellix-webhook/1.0` |
| `X-Modellix-Event` | The event type that triggered the webhook |
| `X-Modellix-Task-ID` | The unique ID of the generation task |
| `X-Modellix-Delivery-ID` | The unique ID of this webhook delivery attempt |
| `X-Modellix-Retry-Count` | The number of retries for this delivery (starts at `0`) |

#### Event Types

The `X-Modellix-Event` header can have one of the following values:

* `prediction.task.succeeded` — Sent when the task completes successfully.
* `prediction.task.failed` — Sent when the task fails.
* `prediction.task.canceled` — Sent when the task is canceled.

### Callback Payload

The webhook request body structure is identical to the response of the [Query Task Result](/api/get-task-result) API.

<CodeGroup>
  ```json Success Example theme={null}
  {
    "code": 0,
    "message": "success",
    "data": {
      "status": "success",
      "task_id": "task_id",
      "model_id": "provider/model",
      "duration": 12345,
      "result": {},
      "billing": {
        "status": "succeeded",
        "amount": "12.3456"
      }
    }
  }
  ```

  ```json Failure Example theme={null}
  {
    "code": 0,
    "message": "success",
    "data": {
      "status": "failed",
      "task_id": "task_id",
      "model_id": "provider/model",
      "error": "provider timeout"
    }
  }
  ```
</CodeGroup>

### Receiver Response Requirements

To acknowledge receipt of the webhook, your server must return an HTTP `2xx` status code.

* **Recommended response:** `HTTP/1.1 200 OK` with a plain text body of `ok`.
* **Alternative response:** `HTTP/1.1 204 No Content` with an empty body.

<Note>
  The Modellix webhook system only checks the HTTP status code and does not parse or validate the response body.
</Note>

### Retry Rules

If delivery fails, the system will attempt to redeliver the webhook under specific conditions.

#### Retried Errors

The system will automatically retry delivery for the following errors:

* HTTP status `429` (Too Many Requests)
* HTTP status `5xx` (Server Errors)
* Network timeouts
* Temporary network errors

#### Non-Retried Errors

The system will **not** retry delivery for the following errors:

* HTTP status `3xx` (Redirection)
* HTTP status `4xx` (Client Errors, except `429`)
* Invalid Webhook URLs
* URLs pointing to private networks or `localhost`
* Permanent connection errors (e.g., connection refused, unresolved DNS)

### Best Practices

To ensure reliable and secure webhook processing, we recommend following these guidelines:

1. **Idempotency:** Use the `X-Modellix-Delivery-ID` header to deduplicate incoming webhooks and prevent processing the same event multiple times.
2. **Asynchronous Processing:** To avoid timeouts, quickly persist the incoming payload or push it to a message queue, and immediately return a `200` or `204` response. Perform any heavy business logic asynchronously.
3. **Security:** Verify the request source or signature (if signature verification is supported in a future update).

## Error Handling

All error responses follow a unified JSON format:

```json theme={null}
{
  "code": 400,
  "message": "Invalid parameters: parameter 'prompt' is required"
}
```

* `code` (integer) — equals the HTTP status code (`0` on success)
* `message` (string) — formatted as `"<Category>: <detail>"`

### Error Codes

| HTTP Status | Description | Common Scenarios | Retryable |
| - | - | - | - |
| 400 | Bad Request | Missing required parameters, invalid format, invalid values | No — fix parameters first |
| 401 | Unauthorized | Invalid API key, missing API key, expired API key | No — provide a valid key |
| 402 | Payment Required | Insufficient balance, account in arrears | No — recharge your account |
| 404 | Not Found | Task ID not found, model not found, provider not found | No — check resource ID |
| 429 | Too Many Requests | Rate limit exceeded, concurrent limit exceeded | Yes — use exponential backoff |
| 500 | Internal Server Error | Internal processing error, unexpected error | Yes — retry up to 3 times |
| 503 | Service Unavailable | Service temporarily unavailable, circuit breaker open | Yes — retry with backoff |

<Tip>
  For **429** responses, check the `X-RateLimit-Reset` header to know when you can retry. Use exponential backoff (1 s → 2 s → 4 s) for **500** and **503** errors.
</Tip>

<h2 id="upload-media-files">
  Upload Media Files
</h2>

Use the File API to upload images, videos, and audio that you can pass into Modellix prediction APIs. Uploads are authenticated with your API Key and are not billed. Files are retained for a limited time (default **7 days**).

<CardGroup cols={3}>
  <Card title="Upload Media File" icon="upload" href="/api/upload-media-file">
    Upload a single file via `multipart/form-data`.
  </Card>

  <Card title="List Media Files" icon="list" href="/api/list-media-files">
    List non-expired files for your team.
  </Card>

  <Card title="Delete Media File" icon="trash" href="/api/delete-media-file">
    Delete a file and free upload quota immediately.
  </Card>
</CardGroup>

### Typical Workflow

<Steps>
  <Step title="Upload a File">
    Send a `POST` request to `/api/v1/media/files` with `multipart/form-data`. The form field name must be `file`.

    The file must use a supported extension and contain valid content for that type.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST 'https://api.modellix.ai/api/v1/media/files' \
        -H 'Authorization: Bearer $API_KEY' \
        -F 'file=@./image.png'
      ```

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

      resp = requests.post(
          "https://api.modellix.ai/api/v1/media/files",
          headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
          files={"file": open("image.png", "rb")},
      )
      print(resp.json())
      ```
    </CodeGroup>

    On success, the response includes a `file_id`, media `type` (`image`, `video`, or `audio`), and a `url`:

    ```json theme={null}
    {
      "code": 0,
      "message": "success",
      "data": {
        "file_id": "550e8400-e29b-41d4-a716-446655440000",
        "type": "image",
        "url": "https://file.modellix.ai/example/550e8400-e29b-41d4-a716-446655440000.png",
        "filename": "image.png",
        "size": 102400,
        "created_at": 1784084400000
      }
    }
    ```
  </Step>

  <Step title="Use the File URL in a Model API">
    Pass `data.url` into the image, video, or audio input fields of a prediction API (for example `image_url`).

    ```bash theme={null}
    curl --request POST \
      --url https://api.modellix.ai/api/v1/alibaba/qwen-image-edit/async \
      --header 'Authorization: Bearer $API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "prompt": "Change the background to a sunny garden",
        "image_url": "https://file.modellix.ai/example/550e8400-e29b-41d4-a716-446655440000.png"
      }'
    ```

    <Tip>
      Prefer the returned `url` over hosting the asset yourself when you only need a temporary public URL for model input.
    </Tip>
  </Step>

  <Step title="List Files (Optional)">
    List media files that belong to your team and have not yet expired. Default page size is `100`; `limit` is capped at `100`.

    ```bash theme={null}
    curl -X GET 'https://api.modellix.ai/api/v1/media/files?limit=10&offset=0' \
      -H 'Authorization: Bearer $API_KEY'
    ```

    ```json theme={null}
    {
      "code": 0,
      "message": "success",
      "data": {
        "items": [
          {
            "file_id": "550e8400-e29b-41d4-a716-446655440000",
            "type": "image",
            "url": "https://file.modellix.ai/example/550e8400-e29b-41d4-a716-446655440000.png",
            "filename": "image.png",
            "size": 102400,
            "created_at": 1784084400000
          }
        ],
        "total": 1,
        "limit": 100,
        "offset": 0
      }
    }
    ```
  </Step>

  <Step title="Delete Files You No Longer Need">
    Delete a file you no longer need. After deletion, it no longer counts toward your upload limit.

    ```bash theme={null}
    curl -X DELETE 'https://api.modellix.ai/api/v1/media/files/550e8400-e29b-41d4-a716-446655440000' \
      -H 'Authorization: Bearer $API_KEY'
    ```

    ```json theme={null}
    {
      "code": 0,
      "message": "success",
      "data": {}
    }
    ```

    <Note>
      Returns `404` if the file is not found.
    </Note>
  </Step>
</Steps>

### Limits and Supported Formats

#### Default Limits

| Limit | Default |
| - | - |
| Max file size | 16 MB |
| Files per team | 10 |
| Concurrent uploads per team | 2 |
| Retention | About 7 days |

<Warning>
  Files expire after the retention window. Delete unused files early if you are approaching the per-team file count limit.
</Warning>

#### Allowed File Extensions

| Category | Extensions |
| - | - |
| image | `jpg`, `jpeg`, `png`, `webp`, `gif`, `bmp`, `tiff`, `tif`, `heic`, `heif` |
| video | `mp4`, `m4v`, `webm`, `mov`, `mkv`, `avi` |
| audio | `mp3`, `wav`, `m4a`, `aac`, `ogg`, `flac`, `opus`, `weba` |

The response `type` field is derived from the extension and is one of `image`, `video`, or `audio`.

### File API Errors

Error responses use the same JSON shape as other Modellix APIs:

```json theme={null}
{
  "code": 400,
  "message": "Invalid format: unsupported file format"
}
```

| HTTP status | Common causes |
| - | - |
| 400 | Missing `file`, unsupported format, content mismatch, invalid image, or rejected content |
| 401 | Missing or invalid API Key |
| 404 | File not found (delete only) |
| 413 | File exceeds the maximum allowed size |
| 429 | Upload quota or concurrency limit exceeded |
| 500 | Internal server error |

Full request and response schemas: [Upload Media File](/api/upload-media-file), [List Media Files](/api/list-media-files), [Delete Media File](/api/delete-media-file).
