Перейти к содержанию

Быстрый старт

Сквозной сценарий: от регистрации до готового фото. Каждый шаг — реальный вызов /v1. Скопируй и выполни по порядку.

export BASE_URL=https://api.mivio.example   # выданный адрес стенда / прода

1. Получить токены

Регистрация выдаёт сразу пару токенов и приветственные кредиты на баланс.

curl -s -X POST "$BASE_URL/v1/auth/register" \
  -H 'Content-Type: application/json' \
  -d '{"email":"demo@example.com","password":"s3cret-pass","name":"Demo"}'
{
  "access_token": "eyJhbGci...",
  "refresh_token": "9f2c...",
  "expires_in": 900,
  "user": { "id": "…", "email": "demo@example.com", "locale": "ru", "balance": 50 }
}

Сохрани access_token — он идёт в каждый защищённый запрос:

export TOKEN=eyJhbGci...

Уже есть аккаунт

Используй POST /v1/auth/login с теми же полями email/password, либо вход через Google / Apple / VK ID / Яндекс ID — см. Аутентификация.

2. Выбрать эффект

Каталог публичный, токен не нужен. У каждого эффекта есть id и cost (в кредитах).

curl -s "$BASE_URL/v1/effects?limit=5"
{
  "items": [
    { "id": "1a2b3c4d-…", "slug": "anime", "name": "Аниме", "cost": 5,
      "preview_url": "https://…/anime.jpg" }
  ],
  "total": 42, "limit": 5, "offset": 0
}
export EFFECT_ID=1a2b3c4d-…

3. Проверить баланс (необязательно)

curl -s "$BASE_URL/v1/credits/balance" -H "Authorization: Bearer $TOKEN"
# {"balance": 50, "plan_credits": 0, "plan_expires_at": null}

Если кредитов меньше, чем cost эффекта, шаг 5 вернёт 402 insufficient_credits.

4. Загрузить фото

Загрузка двухшаговая: сначала просим подписанную форму (presigned POST), потом льём байты напрямую в хранилище (не через API). Подробности и подводные камни — Загрузка фото.

# 4a. Запросить форму загрузки
curl -s -X POST "$BASE_URL/v1/uploads" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"content_type":"image/jpeg"}'
{
  "upload_id": "b3f1c2a4-…",
  "url": "https://…/uploads",
  "fields": { "Content-Type": "image/jpeg", "key": "…", "policy": "…", "x-amz-…": "…" },
  "expires_in": 600
}
# 4b. Залить файл: multipart-POST на url — все fields как есть, файл полем file ПОСЛЕДНИМ.
curl -s -X POST "<url>" \
  -F 'Content-Type=<из fields>' -F 'key=<из fields>' -F 'policy=<из fields>' \
  -F 'x-amz-algorithm=<из fields>' -F 'x-amz-credential=<из fields>' \
  -F 'x-amz-date=<из fields>' -F 'x-amz-signature=<из fields>' \
  -F 'file=@photo.jpg'

export UPLOAD_ID=b3f1c2a4-…

5. Создать генерацию → 202

curl -s -X POST "$BASE_URL/v1/generations" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{\"effect_id\":\"$EFFECT_ID\",\"characters\":[[\"$UPLOAD_ID\"]]}"
{ "id": "7c9e…", "status": "pending", "cost": 5 }

cost уже зарезервирован с баланса. id — ручка задачи на следующий шаг.

В приложении шли этот запрос с заголовком Idempotency-Key: если ответ не дошёл, повтор с тем же ключом вернёт ту же генерацию и не спишет кредиты второй раз (подробнее).

export GEN_ID=7c9e…

6. Дождаться результата (SSE) и забрать фото

Подписываемся на статус — стрим присылает события сам, поллить не нужно:

curl -N "$BASE_URL/v1/generations/$GEN_ID/stream" -H "Authorization: Bearer $TOKEN"
event: status
data: {"id":"7c9e…","status":"processing","error_code":null}

event: status
data: {"id":"7c9e…","status":"succeeded","error_code":null}

Как только статус стал succeeded — забираем деталь с готовой картинкой:

curl -s "$BASE_URL/v1/generations/$GEN_ID" -H "Authorization: Bearer $TOKEN"
{
  "id": "7c9e…", "status": "succeeded",
  "effect": { "id": "1a2b3c4d-…", "name": "Аниме", "slug": "anime" },
  "result_url": "https://…/results/…&X-Amz-Signature=…",
  "created_at": "2026-06-03T10:00:00Z", "completed_at": "2026-06-03T10:00:08Z"
}

result_url — presigned-ссылка на готовое фото (живёт около часа; если протухла, просто запроси деталь ещё раз).

Готово

Это весь happy-path. Дальше: как устроен жизненный цикл и realtime, что делать на ошибках и как получать готовность push'ем вместо открытого SSE.