# QOYO Studio API for AI agents

> **Пакет для передачи партнёрам:** `integration/qoyo-agent-api/` в репозитории (README, SKILL, MCP, примеры).

**Base URL:** `https://qoyo.ru/api/v1` (or your `APP_BASE_URL` + `/api/v1`)

**Auth:** `Authorization: Bearer qoyo_sk_…` — personal key from [Account / Cabinet](https://qoyo.ru/cabinet).

**Billing:** Same credit balance as the web studio. Top up in the cabinet (YooKassa). Referral rewards do **not** apply to API usage charges.

**Discount:** Up to 30% off studio pricing (global default, per-user, or per-key — effective rate is the **maximum** of configured values).

**Rate limit:** Per key, requests per minute (see Admin → API).

---

## Quick flow

1. `GET /me` — balance, discount, doc links  
2. `GET /catalog` — models + `uploadDefaults` + `agentWorkflow`  
3. `GET /catalog/{modelId}` — `fields`, `defaults`, **`pricing`**, **`fieldMeta`** (upload limits & billing parameters)  
4. `POST /price` — **`credits`** + **`billingInput`** (required before generate)  
5. `POST /generate` — same `model` + same `input` as step 4  
6. `GET /tasks/{id}?sync=1` — poll until `success` or `fail`

**Agents:** use package `integration/qoyo-agent-api/AGENT_SETUP.md` + `SKILL.md` — do not guess price or file limits.

## Catalog API for agents

`GET /catalog/{modelId}` includes:

| Block | Purpose |
|-------|---------|
| `pricing.billingMode` | How the server calculates cost (`composite_table`, `seedance2_metered`, `video_second_metered`, …) |
| `pricing.billingParameterNames` | Input keys that change price — send all of them to `/price` |
| `pricing.agentNotes` | Model-specific rules (Avatar audio, Seedance modes, …) |
| `fieldMeta[].upload` | `supported`, `maxMb`, `maxFiles`, `acceptMimeTypes` per field |
| `fieldMeta[].affectsPricing` | Whether the field changes credits |

`POST /uploads` accepts optional `modelId` + `fieldName` to apply per-field limits from `fieldMeta`.

---

## Kling AI Avatar (billing duration)

Models `kling/ai-avatar-standard` and `kling/ai-avatar-pro`: **duration for pricing is taken from `audio_url`**, same as in the web studio (server probes the file via ffprobe).

- You do **not** need to guess duration — pass `image_url`, `audio_url`, and `prompt`.
- Optional `duration` in `input` is only a fallback if probing fails; if it differs from the audio length, the API **uses the probed value** for `/price` and `/generate`.
- `/price` may return `klingAvatarBilling` with `duration`, `probedFromAudio`, `audioSecondsProbed`, `durationAdjusted`.

Requires **ffprobe** on the server (`FFPROBE_PATH` or `ffprobe` in PATH).

---

## Pricing parameters (all models)

`/price` and `/generate` use the **same** input normalization as the web studio before billing:

| Input shape | Normalized for pricing |
|-------------|-------------------------|
| `duration`, numeric selects | Integers/floats (`"5"` → `5`) when catalog has `valueAsNumber` / `valueAsFloat` |
| `sound`, `nsfw_checker`, booleans | `true` / `false` (also accepts `"true"`, `1`, `"0"`) |
| `reference_*_urls`, `input_urls`, … | JSON array; a single URL string is wrapped as one-element array |
| Catalog defaults | Missing optional fields get defaults from `GET /catalog/{modelId}` |
| Local uploads | `/api/uploads/...` paths are expanded to absolute URLs on the server |
| Required fields | Empty or missing required fields → `422` with field error |

`/price` returns **`billingInput`** — the normalized object used for the estimate (use it to debug mismatches).

**Seedance 2 / 2 Fast:** do not mix first/last frame with reference image/video/audio; video refs add metered input seconds (see catalog `duration`, `resolution`).

**GPT Image 2 / Gemini Omni video:** aspect ratio and resolution rules match the studio (applied inside server pricing).

**Motion Control:** billed by fixed default seconds per orientation (not client `duration`).

**Grok Imagine Video 1.5** (`grok-imagine-video-1-5-preview`): image-to-video; **`video_second_metered`** — `duration` (1–15 s) × rate by `resolution` (`480p`/`720p`), plus flat input-image fee when `image_urls` is set. Required: one image in `image_urls` (max 20 MB).

Extended guide in repo: `integration/qoyo-agent-api/PRICING_AND_INPUT.md`.

---

## Uploads

Prefer **public HTTPS URLs** in model `input` when the user already hosts media.

If you have a local file, use:

`POST /uploads` — `multipart/form-data`: `file`, optional `assetKind` (`image`|`video`|`audio`), optional `modelId`, `fieldName`.

Response includes `absoluteUrl` — pass it in the model input field that expects a URL.

---

## Examples

```bash
export QOYO_API_KEY="qoyo_sk_…"
export BASE="https://qoyo.ru/api/v1"

curl -s -H "Authorization: Bearer $QOYO_API_KEY" "$BASE/me"

curl -s -H "Authorization: Bearer $QOYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","input":{"prompt":"A cat in neon rain"}}' \
  "$BASE/price"

curl -s -H "Authorization: Bearer $QOYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","input":{"prompt":"A cat in neon rain"},"idempotencyKey":"my-run-001"}' \
  "$BASE/generate"

curl -s -H "Authorization: Bearer $QOYO_API_KEY" "$BASE/tasks/TASK_ID?sync=1"
```

---

## Errors

| HTTP | Meaning |
|------|---------|
| 401 | Missing or invalid API key |
| 402 | Insufficient credits |
| 422 | Validation / model error |
| 429 | Rate limit — retry after `Retry-After` |

---

## OpenAPI & MCP

- OpenAPI: `/api/v1/openapi.json`  
- MCP descriptor: `/docs/mcp-qoyo-agent.json`  
- Integration package: `integration/qoyo-agent-api/` in repo  
- Cursor skill: `skills/qoyo-agent-api/SKILL.md`  

---

# API QOYO для ИИ-агентов (RU)

**База:** `https://qoyo.ru/api/v1`

**Ключ:** `Authorization: Bearer qoyo_sk_…` — в [личном кабинете](https://qoyo.ru/cabinet), один ключ на аккаунт.

**Оплата:** тот же баланс кредитов, что в студии. Пополнение — в кабинете. Реферальные начисления за расходы через API **не** действуют.

**Скидка:** до 30% (глобально, на пользователя, на ключ — применяется **максимальная** из настроенных).

**Лимит:** запросов в минуту на ключ (Админ → API).

Шаги: `/me` → `/catalog` → `/catalog/{modelId}` (см. `pricing`, `fieldMeta`) → `/price` (`billingInput`) → `/generate` → `/tasks/{id}?sync=1`. Агентам: `integration/qoyo-agent-api/AGENT_SETUP.md`.

Загрузки: публичные URL в `input` или `POST /uploads` → `absoluteUrl` в поле модели.

**Kling AI Avatar:** длительность для цены считается по `audio_url` автоматически (как в студии); поле `duration` в `input` не обязательно.

**Grok Imagine Video 1.5** (`grok-imagine-video-1-5-preview`): image-to-video; обязателен `image_urls` (1 фото, до 20 MB); цена по `duration`, `resolution` и доплате за изображение.

**Параметры цены:** типы и defaults как в каталоге; `/price` отдаёт `billingInput`. Seedance: не смешивать frame и reference modes. Булевы и duration приводятся на сервере.

**Пакет для интеграции:** папка `integration/qoyo-agent-api/` (передайте целиком).

OpenAPI: `/api/v1/openapi.json` · MCP: `/docs/mcp-qoyo-agent.json`
