# Formula AI > Единый API с клиентским ключом, рублёвым балансом и собственными результатами. Доступны только опубликованные проверенные операции. Базовый URL: https://formula-ai.ru/api/v1 Доступность моделей: GET /api/v1/models с Bearer. Музыкальные методы и параметры описаны в /docs/api/music. Доступность конкретной операции определяется GET /api/v1/models. Streaming текста описан в отдельном гайде. ## Быстрый старт [Быстрый старт](https://formula-ai.ru/docs/quickstart) Зарегистрируйтесь на https://formula-ai.ru/signup, подтвердите email кодом из письма и создайте API-ключ в личном кабинете: https://formula-ai.ru/settings/keys. Ключ показывается один раз при создании или перевыпуске. Выдача через администратора также доступна. Передавайте ключ в Authorization: Bearer; не помещайте в URL. Базовый URL: https://formula-ai.ru/api/v1. Сначала получите доступные модели через GET /models и выберите модель с нужной операцией. verification_level=basic означает подтверждённые реальные результат и расход при сокращённой проверке; full — полную записанную проверку. Отдельные параметры, ошибки и возвраты при basic могут оставаться непроверенными. `unverified` означает нетекстовую операцию, опубликованную по явному разрешению владельца без реального вызова; `unrecorded` — уровень проверки не записан. Проверьте GET /balance, затем отправьте запрос. Если получен HTTP 202, сохраните собственный ID запроса и опрашивайте GET /requests/{id}. Ожидание и финансовая сверка могут продолжаться после окончания HTTP-соединения. Оплачиваемую повторную отправку защищайте заголовком Idempotency-Key. Повтор с тем же содержимым вернёт тот же запрос; изменение параметров с прежним ключом даст 409. Для длительного запроса добавьте Prefer: respond-async: сервис быстро сохранит операцию и вернёт HTTP 202 с id, X-Generation-ID и Location. Получайте полный готовый результат через GET /requests/{id}. Без этого заголовка синхронные клиенты продолжают получать прежний формат ответа. ### cURL · GET /balance ```bash curl "https://formula-ai.ru/api/v1/balance" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### cURL · POST /chat/completions ```bash curl "https://formula-ai.ru/api/v1/chat/completions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-text-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply OK\" } ] }" ``` ### PHP · POST /chat/completions ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "messages" => [["role" => "user", "content" => "Reply OK"]]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: first-text-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /balance ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ## Текст и инструменты [Текст и инструменты](https://formula-ai.ru/docs/guides/text) POST /chat/completions принимает выбранную модель и messages в формате диалога. Роли: system, developer, user, assistant, tool. Для tool-сообщения обязателен tool_call_id. Поддержка tools, response_format, reasoning и лимитов зависит от модели и выбранного маршрута. Неизвестный или несовместимый параметр отклоняется до обращения к модели. Инструменты возвращаются клиенту; сервис не исполняет клиентские функции. Обычный запрос использует stream: false. Для потоковой выдачи текста передайте stream: true; протокол и повтор запроса описаны в гайде Streaming. Обычный результат содержит choices и usage. Завершение по лимиту токенов может вернуть пустой текст; проверяйте finish_reason и задавайте достаточный лимит. Длительный текст: Prefer: respond-async, прежние model/messages и Idempotency-Key; затем GET /requests/{id}. При completed используйте result. Ограничения клиентского соединения не отменяют уже принятую генерацию. Синхронное ожидание допускает общий сохранённый срок операции; клиент и прокси должны разрешать такое ожидание. ### cURL · POST /chat/completions ```bash curl "https://formula-ai.ru/api/v1/chat/completions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-text-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply OK\" } ] }" ``` ### PHP · POST /chat/completions ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "messages" => [["role" => "user", "content" => "Reply OK"]]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: first-text-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ## Изображения [Изображения](https://formula-ai.ru/docs/guides/images) POST /images или /images/generations создаёт изображение. Для генерации требуется prompt; для правки передавайте input_references. Вариации без prompt доступны только у маршрутов, подтверждающих такую возможность; несовместимое сочетание отклоняется до генерации. Доступные размеры, качество, число изображений и маска зависят от модели. Входной файл — HTTPS URL или data URI base64. Изображения-референсы передаются как строки либо объекты type=image_url, image_url.url. Сервер проверяет формат и размер, запрещает локальные и частные адреса. response_format=url возвращает адрес файла Formula AI; для получения файла передайте тот же Bearer. response_format=b64_json возвращает base64 из собственного хранилища. Технические адреса поставщиков клиенту не выдаются. Модели с операцией image_chat также принимают messages через /chat/completions; результат может содержать choices.message.images. HTTP 202 означает ожидание сохранения результата: продолжайте опрос собственного запроса. Проверяйте срок хранения: истёкший файл возвращает 410. Численные ограничения задаёт администратор; расширение размеров не означает поддержку конкретной моделью. ### cURL · POST /images ```bash curl "https://formula-ai.ru/api/v1/images" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-images-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A red circle on a white background\", \"response_format\": \"url\" }" ``` ### cURL · POST /images ```bash curl "https://formula-ai.ru/api/v1/images" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-image_edit-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"Change the background to blue\", \"input_references\": [ { \"type\": \"image_url\", \"image_url\": { \"url\": \"https://example.com/source.png\" } } ] }" ``` ### PHP · POST /images ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "prompt" => "A red circle on a white background"], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-images-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /images ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "prompt" => "A red circle on a white background", "input_references" => [getenv("AI_SERVICE_SOURCE_URL")]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-image_edit-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ## Streaming текста, изображений и аудио [Streaming текста, изображений и аудио](https://formula-ai.ru/docs/guides/streaming) POST /chat/completions с stream: true возвращает Content-Type: text/event-stream. Части имеют object=chat.completion.chunk, собственные id/model и choices.delta. Несовместимый маршрут отклоняется до генерации; поддержка определяется опубликованной операцией модели. Событие — строка data: JSON и пустая строка. Текст приходит в delta.content; tool_calls.arguments может состоять из нескольких частей. Поток завершается data: [DONE]. Собирайте части по choices.index и tool_calls.index, не исполняйте незавершённые аргументы. POST /images с stream: true передаёт события image_generation.partial_image и image_generation.completed с data[], затем image_generation.settled с итоговым статусом и расчётом. Промежуточные кадры зависят от модели; полный JSON поставщика даёт одно событие completed. Файлы выдаются через защищённые адреса Formula AI либо запрошенный response_format=b64_json. Для аудио передайте modalities=["text","audio"], audio.voice и audio.format="pcm16", stream=true. delta.audio.data содержит независимый base64-фрагмент: сначала декодируйте каждый фрагмент, затем объединяйте байты. delta.audio.transcript содержит части расшифровки. PCM16 — знаковые 16-битные значения little-endian, моно, 24000 Гц; контейнер WAV/MP3 автоматически не добавляется. После completed запросите GET /requests/{id}: choices.message.audio содержит приватный url, format, sample_rate, channels и transcript. Голоса указаны в схеме запроса. stream_options: {"include_usage": true} включает клиентский usage. Последнее JSON-событие содержит ai_service.status и billing. DONE означает закрытие соединения: проверяйте status и события error. Стоимость поставщика, его модель, ключ и адрес не выдаются. Ошибка до начала ответа сохраняет обычный HTTP/JSON-формат. После первой части HTTP-статус уже не меняется: ошибка передаётся в data.error, затем итоговый расчёт и DONE. После начала выдачи новая попытка у другого ключа или поставщика не создаётся. Idempotency-Key сохраняет одну операцию. Повтор завершённого потока воспроизводит сохранённые части без нового расхода. Пока первоначальная операция выполняется, повтор возвращает HTTP 202 с собственным статусом; используйте GET /requests/{id}. Повтор после очистки содержимого даёт 410. При отключении клиента сервис продолжает получать результат и учитывает фактический расход. Обрыв внешнего потока оставляет частичный ответ и pending-сверку стоимости; отправленная генерация не повторяется при восстановлении воркера. Поддержка потока зависит от опубликованного профиля модели. ID передаётся сразу: заголовок X-Generation-ID и первый data-объект с собственным id и ai_service.status_url, для текста choices=[]; для изображений type=image_generation.accepted. Это подтверждение принятия, а не текст модели. Комментарии SSE : keep-alive поддерживают соединение во время ожидания и игнорируются сборщиком data-событий. При отключении клиента обработка и сохранение результата продолжаются. GET /requests/{id} возвращает полный собранный result после completed. Повтор с тем же ключом воспроизводит сохранённые части и следует за ещё выполняемой операцией без новой генерации. Prefer: respond-async вместе со stream=true возвращает JSON 202; поток поставщика сохраняется в фоне. ### cURL · POST /chat/completions ```bash curl "https://formula-ai.ru/api/v1/chat/completions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-stream-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply OK\" } ], \"stream\": true, \"stream_options\": { \"include_usage\": true } }" \ --no-buffer ``` ### cURL · POST /images ```bash curl "https://formula-ai.ru/api/v1/images" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-image_stream-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A red circle on a white background\", \"stream\": true }" \ --no-buffer ``` ### cURL · POST /chat/completions ```bash curl "https://formula-ai.ru/api/v1/chat/completions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-audio_stream-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply OK\" } ], \"modalities\": [ \"text\", \"audio\" ], \"audio\": { \"voice\": \"alloy\", \"format\": \"pcm16\" }, \"stream\": true, \"stream_options\": { \"include_usage\": true } }" \ --no-buffer ``` ### PHP · POST /chat/completions · SSE ```php true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . getenv('AI_SERVICE_API_KEY'), 'Idempotency-Key: php-stream-1'], CURLOPT_POSTFIELDS => json_encode(['model' => getenv('AI_SERVICE_MODEL'), 'messages' => [['role' => 'user', 'content' => 'Reply OK']], 'stream' => true, 'stream_options' => ['include_usage' => true]], JSON_THROW_ON_ERROR), CURLOPT_WRITEFUNCTION => function ($handle, string $bytes) use (&$buffer, &$done, &$completed, &$audioBytes): int { $buffer .= $bytes; while (($position = strpos($buffer, "\n\n")) !== false) { $event = substr($buffer, 0, $position); $buffer = substr($buffer, $position + 2); if (!str_starts_with($event, 'data: ')) { continue; } $data = substr($event, 6); if ($data === '[DONE]') { $done = true; continue; } $chunk = json_decode($data, true, 512, JSON_THROW_ON_ERROR); foreach ($chunk['choices'] ?? [] as $choice) { echo $choice['delta']['content'] ?? ''; if (isset($choice['delta']['audio']['data'])) { $part = base64_decode($choice['delta']['audio']['data'], true); if ($part === false) { throw new RuntimeException('Invalid audio base64'); } $audioBytes .= $part; } echo $choice['delta']['audio']['transcript'] ?? ''; } if (isset($chunk['type'], $chunk['data'])) { echo json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n"; } if (isset($chunk['ai_service'])) { $completed = $chunk['ai_service']['status'] === 'completed'; } if (isset($chunk['error']) || isset($chunk['ai_service'])) { fwrite(STDERR, json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n"); } } return strlen($bytes); }, ]); if (getenv('AI_SERVICE_CA')) { curl_setopt($handle, CURLOPT_CAINFO, getenv('AI_SERVICE_CA')); } $success = curl_exec($handle); $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); $error = curl_error($handle); curl_close($handle); if (!$success || $status !== 200 || !$done || !$completed) { throw new RuntimeException('HTTP ' . $status . ': ' . $error . ' ' . $buffer); } if ($audioBytes !== '') { file_put_contents(getenv('AI_SERVICE_AUDIO_OUTPUT') ?: 'speech.pcm', $audioBytes); } ``` ### PHP · POST /images · SSE ```php true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . getenv('AI_SERVICE_API_KEY'), 'Idempotency-Key: php-image_stream-1'], CURLOPT_POSTFIELDS => json_encode(['model' => getenv('AI_SERVICE_MODEL'), 'prompt' => 'A red circle on a white background', 'stream' => true], JSON_THROW_ON_ERROR), CURLOPT_WRITEFUNCTION => function ($handle, string $bytes) use (&$buffer, &$done, &$completed, &$audioBytes): int { $buffer .= $bytes; while (($position = strpos($buffer, "\n\n")) !== false) { $event = substr($buffer, 0, $position); $buffer = substr($buffer, $position + 2); if (!str_starts_with($event, 'data: ')) { continue; } $data = substr($event, 6); if ($data === '[DONE]') { $done = true; continue; } $chunk = json_decode($data, true, 512, JSON_THROW_ON_ERROR); foreach ($chunk['choices'] ?? [] as $choice) { echo $choice['delta']['content'] ?? ''; if (isset($choice['delta']['audio']['data'])) { $part = base64_decode($choice['delta']['audio']['data'], true); if ($part === false) { throw new RuntimeException('Invalid audio base64'); } $audioBytes .= $part; } echo $choice['delta']['audio']['transcript'] ?? ''; } if (isset($chunk['type'], $chunk['data'])) { echo json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n"; } if (isset($chunk['ai_service'])) { $completed = $chunk['ai_service']['status'] === 'completed'; } if (isset($chunk['error']) || isset($chunk['ai_service'])) { fwrite(STDERR, json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n"); } } return strlen($bytes); }, ]); if (getenv('AI_SERVICE_CA')) { curl_setopt($handle, CURLOPT_CAINFO, getenv('AI_SERVICE_CA')); } $success = curl_exec($handle); $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); $error = curl_error($handle); curl_close($handle); if (!$success || $status !== 200 || !$done || !$completed) { throw new RuntimeException('HTTP ' . $status . ': ' . $error . ' ' . $buffer); } if ($audioBytes !== '') { file_put_contents(getenv('AI_SERVICE_AUDIO_OUTPUT') ?: 'speech.pcm', $audioBytes); } ``` ### PHP · POST /chat/completions · SSE ```php true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . getenv('AI_SERVICE_API_KEY'), 'Idempotency-Key: php-audio_stream-1'], CURLOPT_POSTFIELDS => json_encode(['model' => getenv('AI_SERVICE_MODEL'), 'messages' => [['role' => 'user', 'content' => 'Reply OK']], 'modalities' => ['text', 'audio'], 'audio' => ['voice' => 'alloy', 'format' => 'pcm16'], 'stream' => true, 'stream_options' => ['include_usage' => true]], JSON_THROW_ON_ERROR), CURLOPT_WRITEFUNCTION => function ($handle, string $bytes) use (&$buffer, &$done, &$completed, &$audioBytes): int { $buffer .= $bytes; while (($position = strpos($buffer, "\n\n")) !== false) { $event = substr($buffer, 0, $position); $buffer = substr($buffer, $position + 2); if (!str_starts_with($event, 'data: ')) { continue; } $data = substr($event, 6); if ($data === '[DONE]') { $done = true; continue; } $chunk = json_decode($data, true, 512, JSON_THROW_ON_ERROR); foreach ($chunk['choices'] ?? [] as $choice) { echo $choice['delta']['content'] ?? ''; if (isset($choice['delta']['audio']['data'])) { $part = base64_decode($choice['delta']['audio']['data'], true); if ($part === false) { throw new RuntimeException('Invalid audio base64'); } $audioBytes .= $part; } echo $choice['delta']['audio']['transcript'] ?? ''; } if (isset($chunk['type'], $chunk['data'])) { echo json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n"; } if (isset($chunk['ai_service'])) { $completed = $chunk['ai_service']['status'] === 'completed'; } if (isset($chunk['error']) || isset($chunk['ai_service'])) { fwrite(STDERR, json_encode($chunk, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n"); } } return strlen($bytes); }, ]); if (getenv('AI_SERVICE_CA')) { curl_setopt($handle, CURLOPT_CAINFO, getenv('AI_SERVICE_CA')); } $success = curl_exec($handle); $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); $error = curl_error($handle); curl_close($handle); if (!$success || $status !== 200 || !$done || !$completed) { throw new RuntimeException('HTTP ' . $status . ': ' . $error . ' ' . $buffer); } if ($audioBytes !== '') { file_put_contents(getenv('AI_SERVICE_AUDIO_OUTPUT') ?: 'speech.pcm', $audioBytes); } ``` ## Видео и фоновые задачи [Видео и фоновые задачи](https://formula-ai.ru/docs/guides/video) POST /videos принимает model, prompt и поддержанные моделью параметры. duration — целое число секунд; resolution, aspect_ratio или size определяют размер. generate_audio — логическое значение. frame_images задаёт опорные кадры: type=image_url, image_url.url, frame_type=first_frame или last_frame. input_references может содержать image_url, video_url или audio_url. Совместимость сочетаний определяется моделью. Seedance 2.5 использует один формат входа для RouterAI и Kie.ai: input_references и frame_images. Прежние reference_image_urls, reference_video_urls, reference_audio_urls, first_frame_url и last_frame_url преобразуются при приёме запроса. Не смешивайте старое и общее представление одного поля. Опорные кадры и референсы не совмещаются. Для Kie.ai последний кадр требует первого. Параметры сохраняются при переключении поставщика: adaptive, 1080p и другие параметры, доступные только отдельному маршруту, не заменяются автоматически. Для редактирования видео в Seedance 2.5 передайте duration=-1, aspect_ratio=adaptive и хотя бы один video_url в input_references без frame_images. Исходное видео должно длиться 4–30 секунд; результат сохраняет длительность и пропорции видео, выбранного моделью для редактирования. Этот режим доступен через совместимые маршруты RouterAI и Kie.ai. Параметры задаёт клиент; сервис не определяет режим по промпту и не заменяет их автоматически. Для исправленного повторного запуска используйте новый Idempotency-Key. Получите HTTP 202 и собственный ID. GET /requests/{id} показывает состояние и результат. Опрос не создаёт новой генерации; повторный POST с тем же Idempotency-Key возвращает тот же запрос. Файлы готовы после состояния completed. Получайте их через адрес Formula AI с Bearer. Не завершённая финансовая сверка отображается отдельно как billing.status=pending. Полная реальная приёмка видео ещё не завершена. Уровень проверки доступен в каталоге моделей; включение маршрута по указанию владельца не означает реального завершения генерации. ### cURL · POST /videos ```bash curl "https://formula-ai.ru/api/v1/videos" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-videos-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A paper boat floating on a quiet pond\" }" ``` ### cURL · GET /requests/REQUEST_ID ```bash curl "https://formula-ai.ru/api/v1/requests/REQUEST_ID" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · POST /videos ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "prompt" => "A red circle on a white background"], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-videos-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /requests/ ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ## Эмбеддинги, rerank и решения [Эмбеддинги, rerank и решения](https://formula-ai.ru/docs/api/utilities) POST /embeddings принимает model и input: строку, массив строк, массив неотрицательных целых токенов или массив таких последовательностей. encoding_format=float либо base64. Необязательные dimensions, user и input_type зависят от модели; input_type: search_query, search_document, classification, clustering. Результат содержит data с векторами в формате модели, usage и ai_service с собственным расчётом. POST /rerank принимает model, query и documents — массив строк. Необязательные top_n и return_documents управляют числом результатов и возвратом текстов. results содержит index и relevance_score. POST /decisions принимает model, state и questions. state — строка, объект или массив контекста. questions — карта имён с type и instructions; noul возвращает вероятность, choice выбирает из criteria, score оценивает 2–10 уровней criteria. Ответы сохраняют имена вопросов в answers. Операции выполняются через тот же баланс, идемпотентность, очередь и историю. Опубликованный маршрут нужен для конкретной модели; импорт схемы не подтверждает реальную интеграцию. HTTP 202 требует опроса /requests/{id}. ### cURL · POST /embeddings ```bash curl "https://formula-ai.ru/api/v1/embeddings" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-embeddings-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"input\": [ \"A short text\", \"Another text\" ], \"encoding_format\": \"float\" }" ``` ### cURL · POST /rerank ```bash curl "https://formula-ai.ru/api/v1/rerank" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-rerank-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"query\": \"A useful guide\", \"documents\": [ \"A guide to integration\", \"A weather report\" ], \"top_n\": 1 }" ``` ### cURL · POST /decisions ```bash curl "https://formula-ai.ru/api/v1/decisions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-decisions-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"state\": \"Please send an integration guide\", \"questions\": { \"needs_guide\": { \"type\": \"noul\", \"instructions\": \"Does the message request a guide?\" } } }" ``` ### PHP · POST /embeddings ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "input" => ["A short text", "Another text"], "encoding_format" => "float"], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-embeddings-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /rerank ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "query" => "A useful guide", "documents" => ["A guide to integration", "A weather report"], "top_n" => 1], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-rerank-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /decisions ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "state" => "Please send an integration guide", "questions" => ["needs_guide" => ["type" => "noul", "instructions" => "Does the message request a guide?"]]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-decisions-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/embeddings": { "post": { "operationId": "createEmbeddings", "summary": "Эмбеддинги текста", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmbeddingInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmbeddingResult" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/rerank": { "post": { "operationId": "createRerank", "summary": "Переранжирование документов", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RerankInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RerankResult" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/decisions": { "post": { "operationId": "createDecisions", "summary": "Структурированные решения", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecisionInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecisionResult" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "Billing": { "type": "object", "properties": { "currency": { "const": "RUB" }, "charged": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "refunded": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "status": { "enum": [ "pending", "confirmed" ] } }, "required": [ "currency", "charged", "refunded", "status" ] }, "Usage": { "type": "object", "description": "Применимые единицы: prompt_tokens, completion_tokens, total_tokens, input_tokens, output_tokens, token_details, images, videos, audio_seconds, duration_seconds. Счётчики могут быть точными численными строками. Поля себестоимости не выдаются." }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "GenerationError": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "error", "request_id", "billing" ] }, "Request": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "ai_service.request" }, "model": { "type": "string" }, "operation": { "type": "string" }, "status": { "enum": [ "queued", "running", "waiting", "saving_media", "uncertain", "completed", "failed", "cancelled" ] }, "created_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "finished_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expires_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expired": { "type": "boolean" }, "usage": { "$ref": "#/components/schemas/Usage" }, "billing": { "$ref": "#/components/schemas/Billing" }, "result": { "type": [ "object", "null" ], "description": "Результат выбранной операции. Файлы — только адреса Formula AI либо запрошенный base64." }, "error": { "anyOf": [ { "$ref": "#/components/schemas/ErrorDetail" }, { "type": "null" } ] } }, "required": [ "id", "object", "model", "operation", "status", "created_at", "finished_at", "content_expires_at", "content_expired", "usage", "billing", "result", "error" ] }, "EmbeddingInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "input": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 1000000 }, { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1, "maxLength": 1000000 } }, { "type": "array", "minItems": 1, "items": { "type": "integer", "minimum": 0 } }, { "type": "array", "minItems": 1, "items": { "type": "array", "minItems": 1, "items": { "type": "integer", "minimum": 0 } } } ] }, "encoding_format": { "enum": [ "float", "base64" ] }, "dimensions": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, "user": { "type": "string", "minLength": 1, "maxLength": 256 }, "input_type": { "enum": [ "search_query", "search_document", "classification", "clustering" ] } }, "required": [ "model", "input" ], "additionalProperties": false }, "RerankInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "query": { "type": "string", "minLength": 1, "maxLength": 1000000 }, "documents": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1, "maxLength": 1000000 } }, "top_n": { "type": "integer", "minimum": 1 }, "return_documents": { "type": "boolean" } }, "required": [ "model", "query", "documents" ], "additionalProperties": false }, "DecisionInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "state": { "type": [ "string", "object", "array" ] }, "questions": { "type": "object", "minProperties": 1, "additionalProperties": { "type": "object", "properties": { "type": { "enum": [ "noul", "choice", "score" ] }, "instructions": { "type": "string", "minLength": 1, "maxLength": 1000000 }, "criteria": { "type": [ "object", "array" ], "description": "noul: необязательные true/false. choice: 1–255 имён с описанием или null. score: 2–10 строк по порядку." } }, "required": [ "type", "instructions" ] } }, "session_id": { "type": "string", "minLength": 1, "maxLength": 256 }, "user": { "type": "string", "minLength": 1, "maxLength": 256 } }, "required": [ "model", "state", "questions" ], "additionalProperties": false }, "EmbeddingResult": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "model": { "type": "string" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "request_id", "billing" ] }, "data": { "type": "array", "items": { "anyOf": [ { "type": "array", "items": { "type": "number" } }, { "type": "object", "properties": { "object": { "type": "string" }, "index": { "type": "integer" }, "embedding": { "type": [ "array", "string" ] } }, "required": [ "embedding" ] } ] } } }, "required": [ "id", "model", "ai_service", "data" ] }, "RerankResult": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "model": { "type": "string" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "request_id", "billing" ] }, "results": { "type": "array", "items": { "type": "object", "properties": { "index": { "type": "integer" }, "relevance_score": { "type": "number" }, "document": { "type": [ "string", "object" ] } }, "required": [ "index", "relevance_score" ] } } }, "required": [ "id", "model", "ai_service", "results" ] }, "DecisionResult": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "model": { "type": "string" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "request_id", "billing" ] }, "answers": { "type": "object", "additionalProperties": { "type": "object" } } }, "required": [ "id", "model", "ai_service", "answers" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## Синтез речи и транскрибация [Синтез речи и транскрибация](https://formula-ai.ru/docs/api/audio) POST /audio/transcriptions принимает JSON: model, input_audio.data с base64 байтов и input_audio.format. Форматы: mp3, wav, flac, m4a, ogg, webm, aac; они также должны быть разрешены политикой файлов. Необязательные language, temperature и response_format=json/verbose_json. timestamp_granularities=word/segment требует verbose_json. Multipart пока не поддерживается. POST /audio/speech принимает model, input и voice. Необязательные response_format=mp3/pcm и speed зависят от модели; исходный формат — pcm. HTTP 200 возвращает бинарные байты audio/mpeg либо audio/pcm. X-Generation-ID — собственный ID для состояния и расчёта через /requests/{id}. HTTP 202 возвращает JSON задачи; файл появится в её результате. Для транскрибации задайте AI_SERVICE_AUDIO_BASE64 и AI_SERVICE_AUDIO_FORMAT; PHP-пример использует файл AI_SERVICE_AUDIO_PATH. Для голоса задайте AI_SERVICE_VOICE. Сохранённое аудио выдаётся только владельцу через /media/{id}; истечение срока хранения не создаёт повторную генерацию. Аудио пока проверено на локальных ответах. Реальные платные проверки и публикация моделей требуют разрешённого объёма тестов и настроенных расчётов. ### cURL · POST /audio/transcriptions ```bash curl "https://formula-ai.ru/api/v1/audio/transcriptions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-transcription-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"input_audio\": { \"data\": \"$AI_SERVICE_AUDIO_BASE64\", \"format\": \"$AI_SERVICE_AUDIO_FORMAT\" } }" ``` ### cURL · POST /audio/speech ```bash curl "https://formula-ai.ru/api/v1/audio/speech" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-speech-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"input\": \"Hello from Formula AI\", \"voice\": \"$AI_SERVICE_VOICE\", \"response_format\": \"mp3\" }" \ --output speech.bin --dump-header speech.headers --write-out "%{http_code}" ``` ### PHP · POST /audio/transcriptions ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "input_audio" => ["data" => base64_encode(file_get_contents(getenv("AI_SERVICE_AUDIO_PATH"))), "format" => getenv("AI_SERVICE_AUDIO_FORMAT")]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-transcription-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /audio/speech ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "input" => "Hello from Formula AI", "voice" => getenv("AI_SERVICE_VOICE"), "response_format" => "mp3"], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-speech-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); if ($status === 200) { file_put_contents("speech.bin", $body); echo "Saved speech.bin"; exit; } $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/audio/transcriptions": { "post": { "operationId": "createTranscription", "summary": "Преобразование речи в текст", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TranscriptionInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TranscriptionResult" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/audio/speech": { "post": { "operationId": "createSpeech", "summary": "Синтез речи", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SpeechInput" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "200": { "description": "Готовые бинарные байты аудио. Состояние и расчёт доступны через GET /requests/{X-Generation-ID}.", "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } }, "content": { "audio/mpeg": { "schema": { "type": "string", "format": "binary" } }, "audio/pcm": { "schema": { "type": "string", "format": "binary" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "Billing": { "type": "object", "properties": { "currency": { "const": "RUB" }, "charged": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "refunded": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "status": { "enum": [ "pending", "confirmed" ] } }, "required": [ "currency", "charged", "refunded", "status" ] }, "Usage": { "type": "object", "description": "Применимые единицы: prompt_tokens, completion_tokens, total_tokens, input_tokens, output_tokens, token_details, images, videos, audio_seconds, duration_seconds. Счётчики могут быть точными численными строками. Поля себестоимости не выдаются." }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "GenerationError": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "error", "request_id", "billing" ] }, "Request": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "ai_service.request" }, "model": { "type": "string" }, "operation": { "type": "string" }, "status": { "enum": [ "queued", "running", "waiting", "saving_media", "uncertain", "completed", "failed", "cancelled" ] }, "created_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "finished_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expires_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expired": { "type": "boolean" }, "usage": { "$ref": "#/components/schemas/Usage" }, "billing": { "$ref": "#/components/schemas/Billing" }, "result": { "type": [ "object", "null" ], "description": "Результат выбранной операции. Файлы — только адреса Formula AI либо запрошенный base64." }, "error": { "anyOf": [ { "$ref": "#/components/schemas/ErrorDetail" }, { "type": "null" } ] } }, "required": [ "id", "object", "model", "operation", "status", "created_at", "finished_at", "content_expires_at", "content_expired", "usage", "billing", "result", "error" ] }, "TranscriptionInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "input_audio": { "type": "object", "properties": { "data": { "type": "string", "minLength": 1, "maxLength": 16000000, "description": "Base64 байтов файла; data URI не принимается." }, "format": { "enum": [ "mp3", "wav", "flac", "m4a", "ogg", "webm", "aac" ] } }, "required": [ "data", "format" ] }, "language": { "type": "string", "pattern": "^[a-z]{2}$" }, "temperature": { "type": "number", "minimum": 0, "maximum": 2 }, "response_format": { "enum": [ "json", "verbose_json" ] }, "timestamp_granularities": { "type": "array", "items": { "enum": [ "word", "segment" ] }, "description": "Только при response_format=verbose_json." } }, "required": [ "model", "input_audio" ], "additionalProperties": false }, "SpeechInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "input": { "type": "string", "minLength": 1, "maxLength": 1000000 }, "voice": { "type": "string", "minLength": 1, "maxLength": 255 }, "response_format": { "enum": [ "mp3", "pcm" ], "description": "По умолчанию pcm; поддержка формата зависит от модели." }, "speed": { "type": "number", "exclusiveMinimum": 0 } }, "required": [ "model", "input", "voice" ], "additionalProperties": false }, "TranscriptionResult": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "model": { "type": "string" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "request_id", "billing" ] }, "text": { "type": "string" }, "task": { "type": "string" }, "language": { "type": "string" }, "duration": { "type": "number" }, "segments": { "type": "array", "items": { "type": "object" } }, "words": { "type": "array", "items": { "type": "object" } } }, "required": [ "id", "model", "ai_service", "text" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## Музыка [Музыка](https://formula-ai.ru/docs/guides/music) Реализован POST /music/generations для текущих версий Suno: suno/v6, suno/v6-mini и suno/v6-wild. Выбирайте доступную модель с операцией music из GET /models; непроверенные сочетания остаются черновиками. Тело содержит model, custom_mode и instrumental. В custom_mode=true укажите title и хотя бы style, lyrics или negative_tags. title — до 80 символов, style — до 1000, lyrics — до 5000. Переносы строк в lyrics сохраняются. В custom_mode=false обязательна хотя бы одна ссылка на изображение/видео/аудио либо style или lyrics. Всего допускается до 10 вложений; до пяти изображений и одного видео. Параметры duration, vocal_gender, style_weight, weirdness_constraint, audio_weight и variety предназначены для пользовательского режима. duration — запрошенная длительность от 10 до 360 секунд. Реальные проверки показали, что фактическая длительность может отличаться; проверяйте duration_seconds каждого трека. Не считайте запрошенную длительность доказательством фактического использования. Создание возвращает собственный ID и HTTP 202. Опрашивайте GET /requests/{id}; completed содержит result.data с собственными id и защищёнными url, названием, текстом песни и фактической длительностью. Файлы хранятся сутки. Денежный результат находится в billing; количество токенов баланса внешнего поставщика не подменяет рублёвую стоимость клиента. Suno поддерживает генерацию и продолжение, кавер и продолжение загруженного аудио, добавление вокала и инструментов, mashup, замену участка, звуковые эффекты и циклы, тексты песен и временные метки, улучшение стиля, Persona, Voice, разделение stems, MIDI, WAV, обложку, клип и восстановление аудиоссылок. Схемы всех 23 методов, включая обязательные поля, перечисления и значения по умолчанию, находятся в /docs/api/music и /openapi.json. ### cURL · POST /music/generations ```bash curl "https://formula-ai.ru/api/v1/music/generations" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"custom_mode\": true, \"instrumental\": true, \"style\": \"Calm minimalist piano\", \"title\": \"Integration check\" }" ``` ### cURL · GET /requests/REQUEST_ID ```bash curl "https://formula-ai.ru/api/v1/requests/REQUEST_ID" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · POST /music/generations ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'custom_mode' => true, 'instrumental' => true, 'style' => 'Calm minimalist piano', 'title' => 'Integration check', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /requests/ ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ## Баланс, расчёты и повторы [Баланс, расчёты и повторы](https://formula-ai.ru/docs/guides/billing) Денежные значения передаются строками в рублях с 7 знаками после точки. GET /balance возвращает balance, reserved и available; доступный остаток учитывает резерв незавершённых запросов. Резервирование отключено: деньги списываются по фактическому использованию. Отрицательный итоговый баланс допускается, но при любом отрицательном остатке новая генерация запрещена. При балансе меньше 100 ₽ допускается до трёх незавершённых запросов по всем операциям. Расход может включать несколько фактически оплаченных попыток. Ошибка сама по себе не означает бесплатную операцию: подтверждённое списание отражается в истории. billing.charged — общий клиентский расход запроса; billing.refunded — учтённые возвраты. Поздний или частичный возврат появляется отдельной операцией по исходным условиям запроса. Повтор сверки не дублирует деньги. billing.status=pending означает, что расчёт ещё уточняется. Неизвестная стоимость не превращается в установленный ноль. По запросу, завершившемуся ошибкой, также проверяйте финансовую историю. GET /balance/transactions показывает собственные денежные операции. GET /requests и /usage — собственную историю. Чужие запросы и файлы возвращают 404. Idempotency-Key: от 1 до 191 символа; разрешены латинские буквы, цифры, точка, дефис, подчёркивание и двоеточие. Область ключа — клиент. Повтор после очистки содержимого возвращает 410, сохраняя финансовую историю и идентичность операции. ### cURL · GET /balance ```bash curl "https://formula-ai.ru/api/v1/balance" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### cURL · GET /balance/transactions?page=1 ```bash curl "https://formula-ai.ru/api/v1/balance/transactions?page=1" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · GET /balance ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /balance/transactions?page=1 ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ## POST /estimate: приблизительная стоимость [POST /estimate: приблизительная стоимость](https://formula-ai.ru/docs/api/estimate) Перед генерацией можно отправить operation и input — обычное тело выбранной операции. Запрос требует клиентский Bearer, не создаёт задачу, не резервирует баланс и не обращается к платному методу поставщика. Необязательный estimated_usage содержит целые количества по названиям тарифов, например prompt и completion. Без него текст приблизительно оценивается по числу символов и сообщений; max_tokens задаёт предел оцениваемого выхода. Изображения, инструменты и неизвестные единицы требуют подтверждённого количества. status=estimated возвращает minimum и maximum в рублях с применённой наценкой. Это приблизительные значения, а не гарантия итоговой цены. Фактический расход может включать несколько оплаченных попыток fallback. status=unknown возвращает null в обеих суммах и missing с недостающими данными. Неизвестная цена не выдаётся за нулевую. Используются только сохранённые тарифы и подтверждённый рублёвый курс. ### cURL · POST /estimate ```bash curl "https://formula-ai.ru/api/v1/estimate" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-estimate-1" \ --data-binary "{ \"operation\": \"chat\", \"input\": { \"model\": \"$AI_SERVICE_MODEL\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply OK\" } ], \"max_tokens\": 64 } }" ``` ### PHP · POST /estimate ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["operation" => "chat", "input" => ["model" => getenv("AI_SERVICE_MODEL"), "messages" => [["role" => "user", "content" => "Reply OK"]], "max_tokens" => 64]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-estimate-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/estimate": { "post": { "operationId": "estimateCost", "summary": "Предварительная оценка без генерации и списания", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CostEstimateInput" } } } }, "responses": { "200": { "description": "Приблизительная оценка либо неизвестная стоимость.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CostEstimate" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "CostEstimateInput": { "type": "object", "properties": { "operation": { "type": "string" }, "input": { "type": "object", "description": "Обычное тело выбранной операции с публичным model." }, "estimated_usage": { "type": "object", "additionalProperties": { "type": "integer", "minimum": 0 }, "description": "Необязательные количества по названиям тарифов: prompt, completion и другие опубликованные единицы." } }, "required": [ "operation", "input" ] }, "CostEstimate": { "type": "object", "properties": { "object": { "const": "ai_service.cost_estimate" }, "model": { "type": "string" }, "operation": { "type": "string" }, "currency": { "const": "RUB" }, "status": { "enum": [ "estimated", "unknown" ] }, "minimum": { "type": [ "string", "null" ], "pattern": "^[0-9]+\\.[0-9]{7}$" }, "maximum": { "type": [ "string", "null" ], "pattern": "^[0-9]+\\.[0-9]{7}$" }, "estimated_usage": { "type": "object", "additionalProperties": { "type": "integer", "minimum": 0 } }, "assumptions": { "type": "array", "items": { "type": "string" } }, "missing": { "type": "array", "items": { "type": "string" } } }, "required": [ "object", "model", "operation", "currency", "status", "minimum", "maximum", "estimated_usage", "assumptions", "missing" ] }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## GET /models [GET /models](https://formula-ai.ru/docs/api/models) Требуется Bearer. Возвращает object=list, data — массив опубликованных моделей. Поля модели: id, object=model, owned_by, name, operations. Операция содержит id и category. Наличие модели в документации не означает публикацию. Пустой data означает отсутствие доступных проверенных маршрутов. Выбирайте точный id из ответа. ### cURL · GET /models ```bash curl "https://formula-ai.ru/api/v1/models" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · GET /models ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/models": { "get": { "operationId": "listModels", "summary": "Опубликованные модели", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Models" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "Models": { "type": "object", "properties": { "object": { "const": "list" }, "data": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "const": "model" }, "owned_by": { "const": "ai-service" }, "name": { "type": "string" }, "operations": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "category": { "type": "string" }, "verification_level": { "enum": [ "basic", "full", "unrecorded", "unverified" ], "description": "basic — реальный результат и расход подтверждены; все варианты параметров, ошибок и возвратов не проверены. unverified — опубликовано по разрешению владельца без реального вызова." } }, "required": [ "id", "category" ] } } }, "required": [ "id", "object", "owned_by", "name", "operations" ] } } }, "required": [ "object", "data" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## POST /chat/completions, /images, /videos [POST /chat/completions, /images, /videos](https://formula-ai.ru/docs/api/generate) Обязательны Content-Type: application/json и Bearer. Тело — JSON-объект, максимум 16 MiB. model — публичный ID. Заголовок Idempotency-Key рекомендован для повторов. Для /chat/completions обязательны непустые messages. Для /videos обязательны model и prompt. /images принимает prompt и применимые поля изображений. Неподдержанный параметр не игнорируется. HTTP 200: готовый chat/image-результат с собственными id, model и ai_service.request_id/billing. HTTP 202: объект ai_service.request с собственным ID; результат получайте опросом. HTTP 502: завершившаяся ошибка с request_id и billing. Не повторяйте оплачиваемый запрос с новым ключом только из-за HTTP-таймаута. Сначала получите существующий запрос по прежнему ключу или ID. Prefer: respond-async выбирает очередь без ожидания поставщика; сервер подтверждает его заголовком Preference-Applied. Заголовки X-Generation-ID и Location связывают принятый POST с GET /requests/{id}. Предпочтение не входит в параметры генерации: его можно изменить при повторе с тем же Idempotency-Key. Если готовый результат уже сохранён, повтор возвращает его обычным форматом; истёкшее содержимое — 410. ### cURL · POST /chat/completions ```bash curl "https://formula-ai.ru/api/v1/chat/completions" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-text-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply OK\" } ] }" ``` ### cURL · POST /images ```bash curl "https://formula-ai.ru/api/v1/images" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-images-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A red circle on a white background\", \"response_format\": \"url\" }" ``` ### cURL · POST /videos ```bash curl "https://formula-ai.ru/api/v1/videos" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-videos-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A paper boat floating on a quiet pond\" }" ``` ### PHP · POST /chat/completions ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "messages" => [["role" => "user", "content" => "Reply OK"]]], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: first-text-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /images ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "prompt" => "A red circle on a white background"], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-images-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /videos ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL"), "prompt" => "A red circle on a white background"], JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-videos-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/chat/completions": { "post": { "operationId": "createChat", "summary": "Диалог, изображения и аудио", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatResult" } }, "text/event-stream": { "schema": { "type": "string", "description": "При stream=true: data: JSON и пустая строка; завершение data: [DONE]. Последний JSON содержит ai_service.status/billing. После начала выдачи ошибка передаётся в data.error; проверяйте итоговый статус." }, "x-item-schema": { "$ref": "#/components/schemas/StreamChunk" }, "x-error-schema": { "$ref": "#/components/schemas/StreamError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/images": { "post": { "operationId": "createImages", "summary": "Генерация и редактирование изображений", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageResult" } }, "text/event-stream": { "schema": { "type": "string", "description": "При stream=true события data.type: partial_image, completed, settled с префиксом image_generation.; затем [DONE]. Превью зависит от модели." }, "x-item-schema": { "$ref": "#/components/schemas/ImageStreamEvent" }, "x-error-schema": { "$ref": "#/components/schemas/StreamError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/images/generations": { "post": { "operationId": "generateImages", "summary": "Псевдоним /images", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageInput" } } } }, "responses": { "200": { "description": "Готовый результат и клиентский расчёт.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageResult" } }, "text/event-stream": { "schema": { "type": "string", "description": "При stream=true события data.type: partial_image, completed, settled с префиксом image_generation.; затем [DONE]. Превью зависит от модели." }, "x-item-schema": { "$ref": "#/components/schemas/ImageStreamEvent" }, "x-error-schema": { "$ref": "#/components/schemas/StreamError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/videos": { "post": { "operationId": "createVideo", "summary": "Генерация видео", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VideoInput" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/generations": { "post": { "operationId": "createMusic", "summary": "Генерация музыки", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MusicInput" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "Billing": { "type": "object", "properties": { "currency": { "const": "RUB" }, "charged": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "refunded": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "status": { "enum": [ "pending", "confirmed" ] } }, "required": [ "currency", "charged", "refunded", "status" ] }, "Usage": { "type": "object", "description": "Применимые единицы: prompt_tokens, completion_tokens, total_tokens, input_tokens, output_tokens, token_details, images, videos, audio_seconds, duration_seconds. Счётчики могут быть точными численными строками. Поля себестоимости не выдаются." }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "GenerationError": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "error", "request_id", "billing" ] }, "Request": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "ai_service.request" }, "model": { "type": "string" }, "operation": { "type": "string" }, "status": { "enum": [ "queued", "running", "waiting", "saving_media", "uncertain", "completed", "failed", "cancelled" ] }, "created_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "finished_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expires_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expired": { "type": "boolean" }, "usage": { "$ref": "#/components/schemas/Usage" }, "billing": { "$ref": "#/components/schemas/Billing" }, "result": { "type": [ "object", "null" ], "description": "Результат выбранной операции. Файлы — только адреса Formula AI либо запрошенный base64." }, "error": { "anyOf": [ { "$ref": "#/components/schemas/ErrorDetail" }, { "type": "null" } ] } }, "required": [ "id", "object", "model", "operation", "status", "created_at", "finished_at", "content_expires_at", "content_expired", "usage", "billing", "result", "error" ] }, "ChatInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "messages": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/Message" } }, "stream": { "type": "boolean", "default": false, "description": "Потоковая выдача текста или аудио. Маршрут обязан поддерживать этот протокол; аудио требует stream=true." }, "temperature": { "type": "number", "minimum": 0, "maximum": 2 }, "top_p": { "type": "number", "minimum": 0, "maximum": 1 }, "frequency_penalty": { "type": "number", "minimum": -2, "maximum": 2 }, "presence_penalty": { "type": "number", "minimum": -2, "maximum": 2 }, "max_tokens": { "type": "integer", "minimum": 1 }, "max_completion_tokens": { "type": "integer", "minimum": 1 }, "n": { "type": "integer", "minimum": 1 }, "seed": { "type": "integer" }, "stop": { "type": [ "string", "array" ] }, "tools": { "type": "array", "items": { "type": "object" }, "description": "Функции в формате Chat Completions; type=function и function.name/parameters. Поддержка зависит от модели." }, "tool_choice": { "type": [ "string", "object" ] }, "response_format": { "type": "object", "properties": { "type": { "enum": [ "text", "json_object", "json_schema" ] }, "json_schema": { "type": "object" } }, "required": [ "type" ] }, "reasoning": { "type": "object" }, "reasoning_effort": { "type": "string" }, "modalities": { "type": "array", "items": { "type": "string" } }, "image_config": { "type": "object", "properties": { "aspect_ratio": { "type": "string" }, "image_size": { "enum": [ "512", "1K", "2K", "4K" ] } }, "required": [] }, "audio": { "type": "object", "properties": { "voice": { "enum": [ "alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse" ] }, "format": { "const": "pcm16" } }, "required": [ "voice", "format" ], "additionalProperties": false }, "stream_options": { "type": "object", "properties": { "include_usage": { "type": "boolean" } }, "required": [ "include_usage" ], "additionalProperties": false } }, "required": [ "model", "messages" ], "allOf": [ { "if": { "required": [ "stream_options" ] }, "then": { "properties": { "stream": { "const": true } }, "required": [ "stream" ] } }, { "if": { "required": [ "modalities" ], "properties": { "modalities": { "contains": { "const": "audio" } } } }, "then": { "required": [ "stream", "audio" ], "properties": { "stream": { "const": true }, "modalities": { "type": "array", "minItems": 2, "maxItems": 2, "uniqueItems": true, "items": { "enum": [ "text", "audio" ] } } } } } ], "description": "Полный набор полей ограничивается возможностями выбранной модели. Аудио: modalities=[text,audio], audio.voice и audio.format=pcm16, моно 24 кГц. Параметры инструментов, формата ответа и image_config не универсальны." }, "AudioChunk": { "type": "object", "properties": { "data": { "type": "string", "minLength": 1, "contentEncoding": "base64", "description": "Независимый base64 фрагмента PCM16, моно 24 кГц. Декодировать каждый фрагмент отдельно и соединять байты." }, "transcript": { "type": "string" } }, "required": [], "additionalProperties": false }, "AudioResult": { "type": "object", "properties": { "url": { "type": "string", "format": "uri", "description": "Защищённый файл Formula AI; для скачивания нужен исходный клиентский Bearer-ключ." }, "format": { "const": "pcm16" }, "sample_rate": { "const": 24000 }, "channels": { "const": 1 }, "transcript": { "type": "string" } }, "required": [ "url", "format", "sample_rate", "channels" ], "additionalProperties": false }, "StreamDelta": { "type": "object", "properties": { "role": { "type": [ "string", "null" ] }, "content": { "type": [ "string", "null" ] }, "reasoning": { "type": [ "string", "null" ] }, "refusal": { "type": [ "string", "null" ] }, "reasoning_details": { "type": [ "array", "null" ] }, "tool_calls": { "type": [ "array", "null" ] }, "function_call": { "type": [ "object", "null" ] }, "audio": { "anyOf": [ { "$ref": "#/components/schemas/AudioChunk" }, { "type": "null" } ] } }, "required": [], "additionalProperties": false }, "StreamChunk": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "chat.completion.chunk" }, "model": { "type": "string" }, "created": { "type": "integer" }, "choices": { "type": "array", "items": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "delta": { "$ref": "#/components/schemas/StreamDelta" }, "finish_reason": { "type": [ "string", "null" ] }, "logprobs": { "type": [ "object", "null" ] } }, "required": [ "index", "delta" ] } }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "status": { "type": "string" }, "billing": { "$ref": "#/components/schemas/Billing" }, "status_url": { "type": "string", "description": "Путь GET /api/v1/requests/{id}, передаётся в начальном событии." } }, "required": [ "request_id", "status", "billing" ] } }, "required": [ "id", "object", "model", "choices" ] }, "StreamError": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "ImageStreamEvent": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "image.generation.event" }, "model": { "type": "string" }, "type": { "enum": [ "image_generation.partial_image", "image_generation.completed", "image_generation.settled", "image_generation.accepted" ] }, "created": { "type": "integer" }, "image_index": { "type": "integer", "minimum": 0 }, "partial_image_index": { "type": "integer", "minimum": 0 }, "data": { "type": "array", "items": { "type": "object", "properties": { "url": { "$ref": "#/components/schemas/MediaSource" }, "b64_json": { "type": "string" }, "revised_prompt": { "type": "string" } }, "required": [], "anyOf": [ { "required": [ "url" ] }, { "required": [ "b64_json" ] } ] } }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "status": { "type": "string" }, "billing": { "$ref": "#/components/schemas/Billing" }, "status_url": { "type": "string", "description": "Путь GET /api/v1/requests/{id}, передаётся в начальном событии." } }, "required": [ "request_id", "status", "billing" ] } }, "required": [ "id", "object", "model", "type" ] }, "ImageInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "prompt": { "type": "string", "minLength": 1, "maxLength": 100000 }, "input_references": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/Reference" } }, "mask": { "anyOf": [ { "$ref": "#/components/schemas/MediaSource" }, { "type": "object" } ] }, "n": { "type": "integer", "minimum": 1 }, "stream": { "type": "boolean", "default": false }, "stream_options": { "type": "object", "properties": { "include_usage": { "type": "boolean" } }, "required": [ "include_usage" ], "additionalProperties": false }, "size": { "type": "string" }, "aspect_ratio": { "type": "string" }, "resolution": { "type": "string" }, "quality": { "type": "string" }, "background": { "type": "string" }, "output_format": { "type": "string" }, "output_compression": { "type": "integer", "minimum": 0, "maximum": 100 }, "seed": { "type": "integer" }, "response_format": { "enum": [ "url", "b64_json" ] } }, "required": [ "model" ], "anyOf": [ { "required": [ "prompt" ] }, { "required": [ "input_references" ] } ], "allOf": [ { "if": { "required": [ "stream_options" ] }, "then": { "properties": { "stream": { "const": true } }, "required": [ "stream" ] } } ], "description": "Редактирование: input_references. Вариации без prompt допускаются только подтверждённым маршрутом. Маска требует исходного изображения и поддержки моделью. Streaming доступен у совместимого маршрута; превью зависит от модели." }, "VideoInput": { "type": "object", "properties": { "model": { "type": "string", "minLength": 1 }, "prompt": { "type": "string", "minLength": 1, "maxLength": 100000 }, "duration": { "anyOf": [ { "type": "integer", "minimum": 1 }, { "const": -1 } ], "description": "Seedance 2.5: -1 только с видео-референсом и aspect_ratio=adaptive; длительность исходного видео 4–30 секунд. Для остальных режимов — положительное целое." }, "resolution": { "type": "string" }, "aspect_ratio": { "type": "string" }, "size": { "type": "string" }, "generate_audio": { "type": "boolean" }, "seed": { "type": "integer" }, "input_references": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/Reference" } }, "frame_images": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "type": { "const": "image_url" }, "image_url": { "type": "object", "properties": { "url": { "$ref": "#/components/schemas/MediaSource" } }, "required": [ "url" ] }, "frame_type": { "enum": [ "first_frame", "last_frame" ] } }, "required": [ "type", "image_url", "frame_type" ] } }, "reference_image_urls": { "type": "array", "items": { "$ref": "#/components/schemas/MediaSource" }, "deprecated": true, "description": "Seedance 2.5: совместимый прежний формат. Используйте input_references." }, "reference_video_urls": { "type": "array", "items": { "$ref": "#/components/schemas/MediaSource" }, "deprecated": true, "description": "Seedance 2.5: совместимый прежний формат. Используйте input_references." }, "reference_audio_urls": { "type": "array", "items": { "$ref": "#/components/schemas/MediaSource" }, "deprecated": true, "description": "Seedance 2.5: совместимый прежний формат. Используйте input_references." }, "first_frame_url": { "$ref": "#/components/schemas/MediaSource", "deprecated": true, "description": "Seedance 2.5: совместимый прежний формат. Используйте frame_images." }, "last_frame_url": { "$ref": "#/components/schemas/MediaSource", "deprecated": true, "description": "Seedance 2.5: совместимый прежний формат. Используйте frame_images." } }, "required": [ "model", "prompt" ], "description": "Seedance 2.5 использует общий формат RouterAI и Kie.ai. Не смешивайте два представления референсов или кадров. Опорные кадры не совмещаются с референсами; последний кадр Kie.ai требует первого." }, "MusicInput": { "type": "object", "properties": { "prompt": { "type": "string", "description": "A description of the desired audio content. Optional.\n- Custom mode (`custom_mode=true`): When provided, the prompt is used strictly as lyrics and sung in the generated track. Character limits by model:\n - V4: Maximum 3000 characters\n - V4_5, V4_5PLUS, V4_5ALL, V5_5, V5: Maximum 5000 characters\n- Non-custom mode (`custom_mode=false`): When provided, the prompt serves as the core idea. Lyrics are generated automatically based on it and do not strictly match the input. Maximum 3000 characters.\n`prompt` is not required. In non-custom mode, provide at least one of `image_urls`, `video_urls`, `audio_urls`, `style`, or `lyrics`. In custom mode, provide at least one of `style`, `lyrics`, or `negative_tags`. Generation is rejected if all related inputs are empty." }, "style": { "type": "string", "description": "Music style specification for the generated audio.\n- Character limits by model:\n - **V4** (Discontinued): Maximum 200 characters\n - **V4_5 and V4_5PLUS** (Discontinued): Maximum 1000 characters\n - **V4_5ALL** (Discontinued): Maximum 1000 characters\n - **V5_5 and V5** (Discontinued): Maximum 1000 characters\n - **V6, V6_MINI, and V6_WILD**: Maximum 1000 characters\n- Common examples: Jazz, Classical, Electronic, Pop, Rock, Hip-hop, etc.", "maxLength": 1000 }, "title": { "type": "string", "description": "Title for the generated music track.\n- Required in Custom Mode (`custom_mode: true`).\n- Max length: 80 characters.\n- Will be displayed in player interfaces and filenames.", "maxLength": 80 }, "custom_mode": { "type": "boolean", "description": "Determines whether advanced parameter customization is enabled.\n- If `true`: Allows detailed control\n- If `false`: Simplified mode" }, "instrumental": { "type": "boolean", "description": "Determines whether the audio should be instrumental (no lyrics).\n- In Custom Mode (`custom_mode: true`):\n - If `true`: Only `style` and `title` are required.\n - If `false`: `style`, `title`, and `prompt` are required (`prompt` is used as the exact lyrics).\n- In Non-custom Mode (`custom_mode: false`):\n - If `true`: Only `style` and `prompt` are required.\n - If `false`: `style`, `prompt`, and `lyrics` are required (`lyrics` is used as a lyrics attachment and can be used together with the audio description in `prompt`)." }, "negative_tags": { "type": "string", "description": "Music styles or traits to exclude from the generated audio. Optional. Use to avoid specific styles." }, "vocal_gender": { "type": "string", "description": "Vocal gender preference. Optional. Use `m` for male and `f` for female. This parameter is only effective when `custom_mode` is true. In practice, it only increases probability and cannot guarantee male/female voice instructions are followed.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Strength of adherence to the specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Controls experimental/creative deviation. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Relative weight of audio features. Optional. Range 0–1, up to 2 decimal places.\n- `audio_weight` is not supported when there are no vocals", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "style_persona", "voice_persona" ], "description": "Persona model. Only available for V5 (Discontinued), V5.5 (Discontinued), V6, V6_MINI, and V6_WILD." }, "duration": { "type": "number", "default": 20, "minimum": 10, "maximum": 360, "description": "Audio duration. Optional; only valid when `custom_mode` is true and the model is V5_5 (Discontinued), V6, V6_MINI, or V6_WILD." }, "image_urls": { "type": "array", "description": "Image references. Only effective when `custom_mode=false`.\n- Optional\n- Up to 5 images; each image must not exceed 10 MB\n- Supported formats: `jpeg`, `png`, `webp`, and `bmp`", "items": { "type": "string" }, "maxItems": 5 }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "multipleOf": 1, "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Controls the diversity and stylistic variation of generated results. Optional. Range 0–4, integer, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "lyrics": { "type": "string", "description": "Lyrics for the generated audio. Optional.\n- V6: Maximum 5000 characters.\n- When `custom_mode=true`, `lyrics` is used as lyrics first; if `lyrics` is not provided, `prompt` is used as lyrics.\n- When `custom_mode=false`, `lyrics` is used as a lyrics attachment and can be used together with the audio description in `prompt`.", "maxLength": 5000 }, "audio_urls": { "type": "array", "items": { "type": "string" }, "description": "Audio references. Only effective when `custom_mode=false`.\n- Optional\n- Audio duration must be between 6 seconds and 30 minutes; each audio file must not exceed 500 MB", "maxItems": 10 }, "video_urls": { "type": "array", "items": { "type": "string" }, "description": "Video references. Only effective when `custom_mode=false`.\n- Optional\n- Up to 1 video file; each file must not exceed 100 MB, duration must not exceed 241 seconds, with no minimum duration\n- Supported formats: mp4, mov, and webm", "maxItems": 1 }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "custom_mode", "instrumental" ], "additionalProperties": false, "allOf": [ { "if": { "properties": { "custom_mode": { "const": true } }, "required": [ "custom_mode" ] }, "then": { "required": [ "title" ], "properties": { "prompt": { "maxLength": 5000 }, "title": { "minLength": 1 }, "image_urls": { "maxItems": 0 }, "video_urls": { "maxItems": 0 }, "audio_urls": { "maxItems": 0 } }, "anyOf": [ { "required": [ "style" ], "properties": { "style": { "minLength": 1 } } }, { "required": [ "lyrics" ], "properties": { "lyrics": { "minLength": 1 } } }, { "required": [ "negative_tags" ], "properties": { "negative_tags": { "minLength": 1 } } } ] }, "else": { "properties": { "prompt": { "maxLength": 3000 } }, "anyOf": [ { "required": [ "style" ], "properties": { "style": { "minLength": 1 } } }, { "required": [ "lyrics" ], "properties": { "lyrics": { "minLength": 1 } } }, { "required": [ "image_urls" ], "properties": { "image_urls": { "minItems": 1 } } }, { "required": [ "audio_urls" ], "properties": { "audio_urls": { "minItems": 1 } } }, { "required": [ "video_urls" ], "properties": { "video_urls": { "minItems": 1 } } } ], "not": { "anyOf": [ { "required": [ "duration" ] }, { "required": [ "vocal_gender" ] }, { "required": [ "style_weight" ] }, { "required": [ "weirdness_constraint" ] }, { "required": [ "audio_weight" ] }, { "required": [ "variety" ] } ] } } }, { "if": { "properties": { "instrumental": { "const": true } }, "required": [ "instrumental" ] }, "then": { "not": { "required": [ "audio_weight" ] } } } ] }, "Message": { "type": "object", "properties": { "role": { "enum": [ "system", "developer", "user", "assistant", "tool" ] }, "content": { "type": [ "string", "array", "null" ], "description": "Строка или части text, image_url, video_url, input_audio. input_audio содержит format=mp3/wav и data=base64. Пустое/null содержимое допустимо у assistant с tool_calls." }, "name": { "type": "string" }, "tool_call_id": { "type": "string", "description": "Обязателен при role=tool." }, "tool_calls": { "type": "array", "items": { "type": "object" } } }, "required": [ "role" ] }, "MediaSource": { "type": "string", "minLength": 1, "description": "HTTPS URL публичного сервера или data URI base64. Ограничения формата, размера и хранения задаёт администратор. Частные адреса запрещены." }, "Reference": { "anyOf": [ { "$ref": "#/components/schemas/MediaSource" }, { "type": "object", "properties": { "type": { "enum": [ "image_url", "video_url", "audio_url" ] }, "image_url": { "anyOf": [ { "$ref": "#/components/schemas/MediaSource" }, { "type": "object", "properties": { "url": { "$ref": "#/components/schemas/MediaSource" } }, "required": [ "url" ] } ] }, "video_url": { "anyOf": [ { "$ref": "#/components/schemas/MediaSource" }, { "type": "object", "properties": { "url": { "$ref": "#/components/schemas/MediaSource" } }, "required": [ "url" ] } ] }, "audio_url": { "anyOf": [ { "$ref": "#/components/schemas/MediaSource" }, { "type": "object", "properties": { "url": { "$ref": "#/components/schemas/MediaSource" } }, "required": [ "url" ] } ] } }, "required": [ "type" ] } ], "description": "Для изображений допускается только image_url. Видео может поддерживать video_url/audio_url." }, "ChatResult": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "model": { "type": "string" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "request_id", "billing" ] }, "choices": { "type": "array", "items": { "type": "object", "properties": { "index": { "type": "integer" }, "message": { "type": "object", "properties": { "role": { "type": "string" }, "content": { "type": [ "string", "null" ] }, "audio": { "$ref": "#/components/schemas/AudioResult" } }, "required": [], "description": "role=assistant, content — текст/null; возможны tool_calls и images с файлами Formula AI. Завершённое аудио содержит собственный URL и параметры PCM16." }, "finish_reason": { "type": [ "string", "null" ] } }, "required": [ "message" ] } } }, "required": [ "id", "model", "ai_service", "choices" ] }, "ImageResult": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "model": { "type": "string" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ai_service": { "type": "object", "properties": { "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "request_id", "billing" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string" }, "b64_json": { "type": "string" }, "revised_prompt": { "type": "string" } }, "required": [] } } }, "required": [ "id", "model", "ai_service", "data" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## Запросы: статус, история и отмена [Запросы: статус, история и отмена](https://formula-ai.ru/docs/api/requests) ID запроса — 32 шестнадцатеричных символа собственного сервиса. Деталь содержит id, object=ai_service.request, model, operation, status, created_at, finished_at, content_expires_at, content_expired, usage, billing, result, error. Состояния: queued — очередь; running — выполнение; waiting — внешний результат; saving_media — сохранение; uncertain — результат уточняется; completed — результат готов; failed — ошибка; cancelled — отменённый запрос. uncertain не запускает слепую повторную генерацию. billing содержит currency=RUB, charged/refunded как строки с 7 знаками, status=pending или confirmed. Финансовое ожидание отделено от состояния результата. Списки /requests и /usage принимают page от 1 до 1000000, возвращают data и page; размер страницы 50. Содержат только историю владельца ключа. content_expired=true означает, что вход и результат очищены по сроку хранения; деньги и usage остаются. Опрос GET /requests/{id} не резервирует деньги и не вызывает новую генерацию. Если первоначальный ответ потерян, вызовите GET /requests/by-idempotency-key с Authorization: Bearer и прежним Idempotency-Key в заголовке. Ответ — тот же ai_service.request, включая id, status, result и billing. Ключ ищется в пределах клиента, даже через другой его действующий API-ключ. Неизвестный или чужой ключ — 404, отсутствующий или некорректный — 422. После очистки содержимого поиск сохраняет ID и финансовые метаданные. Пример: curl "https://formula-ai.ru/api/v1/requests/by-idempotency-key" -H "Authorization: Bearer $AI_SERVICE_API_KEY" -H "Idempotency-Key: example-text-1". status описывает результат, billing.status — расчёты: completed может иметь billing.status=pending, а uncertain — подтверждённый расход без полного результата. Частичный result при uncertain не считается готовым. Нулевая предварительная запись без usage не подтверждает бесплатное завершение. После разрыва сервис восстанавливает сохранённый завершённый ответ или опрашивает ту же внешнюю задачу, если поставщик поддерживает получение результата. Если доступна только стоимость, uncertain сохраняется до появления доказательства результата; автоматическая новая генерация не выполняется. POST /requests/{id}/cancel с Bearer и без тела отменяет собственный незавершённый запрос. Ответ HTTP 200 содержит статус cancelled; повторная отмена безопасна. Чужой или неизвестный запрос — 404, завершённый completed/failed — 409. До отправки поставщику генерация не создаётся. Для уже отправленного запроса активное HTTP/SSE-соединение прерывается воркером; внешняя остановка не гарантируется. Подтверждённая отмена поставщиком также даёт cancelled и error.code=provider_cancelled. Таймаут и пустые сведения поставщика сами по себе не доказывают отмену. Поздний результат не возобновляет отменённый запрос. Списание и возврат учитываются по фактическим данным, billing.status может оставаться pending. ### cURL · GET /requests/REQUEST_ID ```bash curl "https://formula-ai.ru/api/v1/requests/REQUEST_ID" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### cURL · POST /requests/{id}/cancel ```bash curl -X POST "https://formula-ai.ru/api/v1/requests/$AI_SERVICE_REQUEST_ID/cancel" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### cURL · GET /requests?page=1 ```bash curl "https://formula-ai.ru/api/v1/requests?page=1" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · GET /requests/ ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /requests/ ```php true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /requests?page=1 ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/requests": { "get": { "operationId": "listRequests", "summary": "История запросов", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Requests" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "parameters": [ { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000000, "default": 1 }, "description": "По 50 записей, от новых к старым." } ] } }, "/usage": { "get": { "operationId": "listUsage", "summary": "История использования", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Requests" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "parameters": [ { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000000, "default": 1 }, "description": "По 50 записей, от новых к старым." } ] } }, "/requests/{id}": { "get": { "operationId": "getRequest", "summary": "Состояние собственного запроса", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/PublicId" } } ] } }, "/requests/{id}/cancel": { "post": { "operationId": "cancelRequest", "summary": "Отменить собственный незавершённый запрос", "description": "Тело не требуется. Повторная отмена возвращает тот же cancelled. Отмена завершённого запроса — 409. До отправки поставщику генерация не создаётся; после отправки HTTP/SSE прерывается по сигналу воркера, внешняя остановка не гарантируется. Фактические расходы и возвраты продолжают сверяться; поздний результат не изменяет cancelled.", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/PublicId" } } ], "responses": { "200": { "description": "Запрос отменён в Formula AI. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } } }, "401": { "description": "Неверный или отозванный ключ.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Запрос не найден у владельца ключа.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Запрос уже завершён.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/requests/by-idempotency-key": { "get": { "operationId": "getRequestByIdempotencyKey", "summary": "Найти исходную операцию по ключу клиента", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Ключ исходного POST в пределах владельца Bearer. Чужой или неизвестный ключ — 404; отсутствующий или неверный — 422. Не создаёт запрос и возвращает метаданные даже после очистки содержимого." } ] } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "Billing": { "type": "object", "properties": { "currency": { "const": "RUB" }, "charged": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "refunded": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "status": { "enum": [ "pending", "confirmed" ] } }, "required": [ "currency", "charged", "refunded", "status" ] }, "Usage": { "type": "object", "description": "Применимые единицы: prompt_tokens, completion_tokens, total_tokens, input_tokens, output_tokens, token_details, images, videos, audio_seconds, duration_seconds. Счётчики могут быть точными численными строками. Поля себестоимости не выдаются." }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "Requests": { "type": "object", "properties": { "data": { "type": "array", "maxItems": 50, "items": { "$ref": "#/components/schemas/Request" } }, "page": { "type": "integer", "minimum": 1, "maximum": 1000000 } }, "required": [ "data", "page" ] }, "Request": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "ai_service.request" }, "model": { "type": "string" }, "operation": { "type": "string" }, "status": { "enum": [ "queued", "running", "waiting", "saving_media", "uncertain", "completed", "failed", "cancelled" ] }, "created_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "finished_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expires_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expired": { "type": "boolean" }, "usage": { "$ref": "#/components/schemas/Usage" }, "billing": { "$ref": "#/components/schemas/Billing" }, "result": { "type": [ "object", "null" ], "description": "Результат выбранной операции. Файлы — только адреса Formula AI либо запрошенный base64." }, "error": { "anyOf": [ { "$ref": "#/components/schemas/ErrorDetail" }, { "type": "null" } ] } }, "required": [ "id", "object", "model", "operation", "status", "created_at", "finished_at", "content_expires_at", "content_expired", "usage", "billing", "result", "error" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## GET /media/{id} [GET /media/{id}](https://formula-ai.ru/docs/api/media) Используйте полный собственный URL из результата и Bearer ключ клиента. Ответ содержит бинарные данные, фактический Content-Type, Content-Length, Accept-Ranges: bytes. Файлы размещены вне публичного каталога. Поддержан один Range: bytes=0-1023 или bytes=-1024. Успешный диапазон — HTTP 206; неподдержанный/невозможный — 416. Чужой ID — 404; истёкший файл — 410. Допустимые типы и размеры определяет настройка сервиса. Сохраните файл до content_expires_at или указанного администратором срока хранения результатов. ### cURL · GET /media/MEDIA_ID ```bash curl "https://formula-ai.ru/api/v1/media/MEDIA_ID" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · GET /media/ ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); if ($status !== 200 && $status !== 206) { throw new RuntimeException("HTTP " . $status . ": " . $body); } file_put_contents("result.bin", $body); echo "Saved result.bin"; ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/media/{id}": { "get": { "operationId": "getMedia", "summary": "Собственный сохранённый файл", "parameters": [ { "name": "Range", "in": "header", "schema": { "type": "string" }, "description": "Один диапазон bytes=0-1023 либо bytes=-1024." }, { "name": "id", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/PublicId" } } ], "responses": { "200": { "description": "Бинарный файл. Content-Type соответствует проверенному содержимому.", "headers": { "Content-Length": { "schema": { "type": "integer" } }, "Accept-Ranges": { "schema": { "type": "string", "const": "bytes" } }, "Content-Range": { "schema": { "type": "string" } } }, "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } } }, "206": { "description": "Бинарный файл. Content-Type соответствует проверенному содержимому.", "headers": { "Content-Length": { "schema": { "type": "integer" } }, "Accept-Ranges": { "schema": { "type": "string", "const": "bytes" } }, "Content-Range": { "schema": { "type": "string" } } }, "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } } }, "416": { "description": "Невозможный либо неподдержанный диапазон.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "Срок хранения истёк.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "default": { "description": "Ошибка доступа или файл не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## GET /balance, /balance/transactions [GET /balance, /balance/transactions](https://formula-ai.ru/docs/api/balance) GET /balance: currency=RUB, balance — остаток, reserved — резерв ранее принятых операций, available — доступный остаток. Денежные значения — десятичные строки с 7 знаками. Новые операции выполняются без резерва, списание проводится по фактическому использованию; применяется математическое округление. Отрицательный итог допускается, но новый запрос генерации при любом отрицательном остатке получает 402. При нуле генерация разрешена. При остатке ниже 100 ₽ суммарно разрешено до трёх незавершённых запросов; превышение даёт 429. Повтор с прежним Idempotency-Key возвращает уже принятый запрос. GET /balance/transactions?page=1: data — собственные операции, page — номер страницы, 50 записей. Поля: id, type, amount, reserved_delta, balance_after, reserved_after, request_id, original_operation_id, created_at. Баланс, история и уведомления доступны в личном кабинете: https://formula-ai.ru/settings/billing. Пополнение через Robokassa станет доступно после настройки магазина. До этого баланс может пополнить администратор. Возврат со страницы оплаты сам по себе не подтверждает зачисление; оно проводится по проверенному уведомлению Robokassa. ### cURL · GET /balance ```bash curl "https://formula-ai.ru/api/v1/balance" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### cURL · GET /balance/transactions?page=1 ```bash curl "https://formula-ai.ru/api/v1/balance/transactions?page=1" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" ``` ### PHP · GET /balance ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · GET /balance/transactions?page=1 ```php true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/balance": { "get": { "operationId": "getBalance", "summary": "Баланс клиента", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Balance" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/balance/transactions": { "get": { "operationId": "listTransactions", "summary": "Финансовая история", "responses": { "200": { "description": "Собственные данные клиента.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Transactions" } } } }, "default": { "description": "Ошибка доступа или параметров.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } }, "parameters": [ { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000000, "default": 1 }, "description": "По 50 записей, от новых к старым." } ] } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "Balance": { "type": "object", "properties": { "currency": { "const": "RUB" }, "balance": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "reserved": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "available": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." } }, "required": [ "currency", "balance", "reserved", "available" ] }, "Transactions": { "type": "object", "properties": { "data": { "type": "array", "maxItems": 50, "items": { "$ref": "#/components/schemas/Transaction" } }, "page": { "type": "integer", "minimum": 1, "maximum": 1000000 } }, "required": [ "data", "page" ] }, "Transaction": { "type": "object", "properties": { "id": { "type": [ "string", "integer" ], "description": "Численный идентификатор. MySQL может вернуть его строкой." }, "type": { "enum": [ "manual_set", "manual_add", "manual_adjust", "reserve", "charge", "refund", "release" ] }, "amount": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "reserved_delta": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "balance_after": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "reserved_after": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "request_id": { "type": [ "string", "null" ] }, "original_operation_id": { "type": [ "string", "integer", "null" ] }, "created_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." } }, "required": [ "id", "type", "amount", "reserved_delta", "balance_after", "reserved_after", "request_id", "original_operation_id", "created_at" ] } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` ## Ошибки и ограничения [Ошибки и ограничения](https://formula-ai.ru/docs/api/errors) Ошибка валидации/доступа: error.message, error.type, error.code, error.param и request_id для диагностики. Сообщение о завершившейся генерации содержит собственный request_id и billing. 401 — ключ отсутствует/отозван, клиент отключён; 402 — недостаточно доступного баланса; 403 — доступ запрещён; 404 — маршрут или собственный объект не найден; 405 — неверный HTTP-метод; 409 — конфликт идемпотентности; 410 — истёкшее содержимое; 413 — слишком большое тело; 415 — неверный Content-Type; 422 — параметры/модель/маршрут; 429 — лимит параллельных запросов; 502 — ошибка генерации/результата; 503 — необходимые настройки ещё не заданы. Операция допускается только при опубликованном совместимом маршруте и выбранных настройках. Числа параллелизма, резерва, сетевых сроков и хранения не являются универсальными константами. Регистрация и личный кабинет доступны на https://formula-ai.ru/signup и https://formula-ai.ru/settings/billing. Доступность операций и параметров проверяйте в каталоге и справочнике API. Справочник обновляется вместе с обработчиками. ## Методы Suno [Методы Suno](https://formula-ai.ru/docs/api/music) Зарегистрированы 23 метода Suno. Для каждого ниже приведены схема, обязательные поля и примеры; выполнение доступно только через опубликованный проверенный маршрут модели. Импорт схемы и локальные проверки не означают реальную приёмку всех методов. Задайте AI_SERVICE_MODEL и AI_SERVICE_API_KEY. AI_SERVICE_TASK_ID — ID собственного исходного запроса Formula AI, AI_SERVICE_AUDIO_ID — ID его готового аудиофайла. Внешние идентификаторы поставщика не принимаются. Источник должен принадлежать клиенту, быть завершённым и доступным в течение срока хранения. Исходное подключение сохраняется. AI_SERVICE_SOURCE_URL и AI_SERVICE_SECOND_SOURCE_URL — доступные по HTTPS исходные файлы либо data URI. AI_SERVICE_VERIFY_URL — запись владельцем фразы из validation-phrase. Используйте достаточно длинный исходный трек для указанного отрезка; continue_at должен находиться внутри него, замена — не короче 10 секунд и не больше половины трека. Для MIDI нужен результат separate-vocals. create-voice и regenerate-phrase используют задачу validation-phrase либо regenerate-phrase; check-voice — завершённую create-voice. После получения фразы владелец записывает её сам. Persona передаётся собственным ID запроса, создавшего Persona или Voice. Все POST возвращают HTTP 202 и собственный ID. Опрос GET /requests/{id} не вызывает новую генерацию. Результаты могут содержать аудио, изображения, видео или JSON; медиа скачиваются с тем же Bearer по адресам Formula AI. Итоговый расход и возврат находятся в billing. Продолжение через extend использует ту же версию Suno, что исходный трек. Генерация Persona использует завершённую generate или extend; vocal_start/vocal_end должны задавать отрезок 10–30 секунд внутри трека. Инструментальные upload-cover и upload-extend не принимают lyrics, prompt и vocal_gender. Разделение: separate_vocal — вокал и сопровождение, split_stem — несколько stems, split_stem_advanced — отдельный инструмент из stem_name. Полное перечисление 98 инструментов есть в схеме; stem_name требуется только в advanced. Выберите task_id + audio_id собственного трека либо audio_url загруженного аудио. Для MIDI можно передать только task_id разделения или дополнительно audio_id из result.data.tracks разделения для выбора одного stem. Каждый такой трек имеет собственные id и url. Recovery принимает task_id собственной завершённой музыкальной генерации, даже если локальные файлы и содержимое уже очищены. Создаётся отдельная задача; исходные файлы остаются удалёнными. Успешно восстановленные ссылки сохраняются как новые защищённые файлы с обычным сроком хранения. Срок доступности у поставщика и успешное восстановление не гарантируются. Sounds: sound_loop — цикл, sound_tempo — целое BPM 1–300, sound_key — одна из 24 тональностей в схеме, grab_lyrics — получение текста. Voice: validation-phrase → запись владельцем полученной фразы → create-voice → check-voice; regenerate-phrase выдаёт новую фразу. singer_skill_level: beginner, intermediate, advanced, professional. Persona или Voice передаются в persona_id как собственный ID создавшего их запроса; persona_model выбирает style_persona либо voice_persona. Параметры передачи файлов проверяются до платной отправки: референсы простого режима — изображения JPEG/PNG/WebP/BMP до 10 MB, видео MP4/MOV/WebM до 100 MB и 241 секунды, аудио до 500 MB и от 6 секунд до 30 минут. Upload-cover/upload-extend принимают исходное аудио до восьми минут. Ключи, внутренние ID и адреса поставщика клиенту не передаются. ### cURL · POST /music/generations ```bash curl "https://formula-ai.ru/api/v1/music/generations" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"custom_mode\": true, \"instrumental\": true, \"style\": \"Calm minimalist piano\", \"title\": \"Integration check\" }" ``` ### cURL · POST /music/extend ```bash curl "https://formula-ai.ru/api/v1/music/extend" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_extend-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\", \"continue_at\": 10, \"instrumental\": true }" ``` ### cURL · POST /music/upload-and-cover-audio ```bash curl "https://formula-ai.ru/api/v1/music/upload-and-cover-audio" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_upload_and_cover_audio-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"upload_url\": \"$AI_SERVICE_SOURCE_URL\", \"instrumental\": true }" ``` ### cURL · POST /music/upload-and-extend-audio ```bash curl "https://formula-ai.ru/api/v1/music/upload-and-extend-audio" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_upload_and_extend_audio-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"upload_url\": \"$AI_SERVICE_SOURCE_URL\", \"instrumental\": true }" ``` ### cURL · POST /music/add-instrumental ```bash curl "https://formula-ai.ru/api/v1/music/add-instrumental" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_add_instrumental-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"upload_url\": \"$AI_SERVICE_SOURCE_URL\", \"title\": \"Piano accompaniment\", \"negative_tags\": \"\", \"tags\": \"Calm piano\" }" ``` ### cURL · POST /music/add-vocals ```bash curl "https://formula-ai.ru/api/v1/music/add-vocals" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_add_vocals-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"upload_url\": \"$AI_SERVICE_SOURCE_URL\", \"title\": \"A calm song\", \"negative_tags\": \"\", \"style\": \"Gentle vocals\" }" ``` ### cURL · POST /music/time-stamped-lyrics ```bash curl "https://formula-ai.ru/api/v1/music/time-stamped-lyrics" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_time_stamped_lyrics-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\" }" ``` ### cURL · POST /music/boost-music-style ```bash curl "https://formula-ai.ru/api/v1/music/boost-music-style" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_boost_music_style-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"content\": \"Calm instrumental piano, gentle melody\" }" ``` ### cURL · POST /music/cover-generate ```bash curl "https://formula-ai.ru/api/v1/music/cover-generate" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_cover_generate-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\" }" ``` ### cURL · POST /music/replace-section ```bash curl "https://formula-ai.ru/api/v1/music/replace-section" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_replace_section-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\", \"prompt\": \"Gentle piano passage\", \"tags\": \"Piano\", \"title\": \"Revised passage\", \"infill_start_s\": 0, \"infill_end_s\": 10, \"full_lyrics\": \"An original song about sunrise\" }" ``` ### cURL · POST /music/generate-persona ```bash curl "https://formula-ai.ru/api/v1/music/generate-persona" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_generate_persona-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\", \"name\": \"My music persona\", \"description\": \"Calm piano\", \"vocal_start\": 0, \"vocal_end\": 20 }" ``` ### cURL · POST /music/mashup ```bash curl "https://formula-ai.ru/api/v1/music/mashup" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_mashup-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"upload_url_list\": [ \"$AI_SERVICE_SOURCE_URL\", \"$AI_SERVICE_SECOND_SOURCE_URL\" ], \"style\": \"Calm piano\", \"title\": \"My mashup\" }" ``` ### cURL · POST /music/suno-recovery-audio ```bash curl "https://formula-ai.ru/api/v1/music/suno-recovery-audio" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_suno_recovery_audio-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\" }" ``` ### cURL · POST /music/convert-to-wav-format ```bash curl "https://formula-ai.ru/api/v1/music/convert-to-wav-format" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_convert_to_wav_format-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\" }" ``` ### cURL · POST /music/create-music-video ```bash curl "https://formula-ai.ru/api/v1/music/create-music-video" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_create_music_video-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\" }" ``` ### cURL · POST /music/generate-lyrics ```bash curl "https://formula-ai.ru/api/v1/music/generate-lyrics" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_generate_lyrics-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A nostalgic song about childhood memories and growing up in a small town\" }" ``` ### cURL · POST /music/validation-phrase ```bash curl "https://formula-ai.ru/api/v1/music/validation-phrase" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_validation_phrase-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"voice_url\": \"$AI_SERVICE_SOURCE_URL\", \"vocal_start_s\": 0, \"vocal_end_s\": 10, \"language\": \"ru\" }" ``` ### cURL · POST /music/create-voice ```bash curl "https://formula-ai.ru/api/v1/music/create-voice" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_create_voice-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"verify_url\": \"$AI_SERVICE_VERIFY_URL\", \"voice_name\": \"My verified voice\" }" ``` ### cURL · POST /music/regenerate-phrase ```bash curl "https://formula-ai.ru/api/v1/music/regenerate-phrase" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_regenerate_phrase-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\" }" ``` ### cURL · POST /music/check-voice ```bash curl "https://formula-ai.ru/api/v1/music/check-voice" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_check_voice-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\" }" ``` ### cURL · POST /music/separate-vocals ```bash curl "https://formula-ai.ru/api/v1/music/separate-vocals" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_separate_vocals-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\", \"audio_id\": \"$AI_SERVICE_AUDIO_ID\", \"type\": \"separate_vocal\" }" ``` ### cURL · POST /music/generate-midi-from-audio ```bash curl "https://formula-ai.ru/api/v1/music/generate-midi-from-audio" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_generate_midi_from_audio-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"task_id\": \"$AI_SERVICE_TASK_ID\" }" ``` ### cURL · POST /music/sounds ```bash curl "https://formula-ai.ru/api/v1/music/sounds" \ -H "Authorization: Bearer $AI_SERVICE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: example-music_sounds-1" \ --data-binary "{ \"model\": \"$AI_SERVICE_MODEL\", \"prompt\": \"A gentle chime in a quiet room\" }" ``` ### PHP · POST /music/generations ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'custom_mode' => true, 'instrumental' => true, 'style' => 'Calm minimalist piano', 'title' => 'Integration check', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/extend ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), 'continue_at' => 10, 'instrumental' => true, ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_extend-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/upload-and-cover-audio ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'upload_url' => getenv("AI_SERVICE_SOURCE_URL"), 'instrumental' => true, ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_upload_and_cover_audio-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/upload-and-extend-audio ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'upload_url' => getenv("AI_SERVICE_SOURCE_URL"), 'instrumental' => true, ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_upload_and_extend_audio-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/add-instrumental ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'upload_url' => getenv("AI_SERVICE_SOURCE_URL"), 'title' => 'Piano accompaniment', 'negative_tags' => '', 'tags' => 'Calm piano', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_add_instrumental-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/add-vocals ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'upload_url' => getenv("AI_SERVICE_SOURCE_URL"), 'title' => 'A calm song', 'negative_tags' => '', 'style' => 'Gentle vocals', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_add_vocals-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/time-stamped-lyrics ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_time_stamped_lyrics-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/boost-music-style ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'content' => 'Calm instrumental piano, gentle melody', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_boost_music_style-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/cover-generate ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_cover_generate-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/replace-section ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), 'prompt' => 'Gentle piano passage', 'tags' => 'Piano', 'title' => 'Revised passage', 'infill_start_s' => 0, 'infill_end_s' => 10, 'full_lyrics' => 'An original song about sunrise', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_replace_section-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/generate-persona ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), 'name' => 'My music persona', 'description' => 'Calm piano', 'vocal_start' => 0, 'vocal_end' => 20, ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_generate_persona-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/mashup ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'upload_url_list' => array ( 0 => getenv("AI_SERVICE_SOURCE_URL"), 1 => getenv("AI_SERVICE_SECOND_SOURCE_URL"), ), 'style' => 'Calm piano', 'title' => 'My mashup', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_mashup-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/suno-recovery-audio ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_suno_recovery_audio-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/convert-to-wav-format ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_convert_to_wav_format-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/create-music-video ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_create_music_video-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/generate-lyrics ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'prompt' => 'A nostalgic song about childhood memories and growing up in a small town', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_generate_lyrics-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/validation-phrase ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'voice_url' => getenv("AI_SERVICE_SOURCE_URL"), 'vocal_start_s' => 0, 'vocal_end_s' => 10, 'language' => 'ru', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_validation_phrase-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/create-voice ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'verify_url' => getenv("AI_SERVICE_VERIFY_URL"), 'voice_name' => 'My verified voice', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_create_voice-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/regenerate-phrase ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_regenerate_phrase-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/check-voice ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_check_voice-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/separate-vocals ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), 'audio_id' => getenv("AI_SERVICE_AUDIO_ID"), 'type' => 'separate_vocal', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_separate_vocals-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/generate-midi-from-audio ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'task_id' => getenv("AI_SERVICE_TASK_ID"), ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_generate_midi_from_audio-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### PHP · POST /music/sounds ```php true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(["model" => getenv("AI_SERVICE_MODEL")] + array ( 'prompt' => 'A gentle chime in a quiet room', ), JSON_THROW_ON_ERROR), CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $key, "Content-Type: application/json", "Idempotency-Key: php-music_sounds-1"], ]); if (getenv("AI_SERVICE_CA")) { curl_setopt($handle, CURLOPT_CAINFO, getenv("AI_SERVICE_CA")); } $body = curl_exec($handle); if ($body === false) { throw new RuntimeException(curl_error($handle)); } $status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); curl_close($handle); $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR); echo json_encode(["status" => $status, "result" => $result], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); ``` ### Схемы метода · OpenAPI 3.1 ```json { "openapi": "3.1.1", "info": { "title": "Formula AI API", "version": "1", "description": "Методы нашего сервиса. Доступность модели и её параметров определяется опубликованным проверенным маршрутом. Suno: 23 музыкальные и вспомогательные операции, схемы и ограничения параметров. Streaming текста, изображений и аудио: SSE, сохранённые части и собственный расчёт." }, "servers": [ { "url": "https://formula-ai.ru/api/v1" } ], "security": [ { "ClientKey": [] } ], "paths": { "/music/generations": { "post": { "operationId": "createMusic", "summary": "Генерация музыки", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MusicInput" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/extend": { "post": { "operationId": "create_music_extend", "summary": "Suno: extend", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_extend" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/upload-and-cover-audio": { "post": { "operationId": "create_music_upload_and_cover_audio", "summary": "Suno: upload and cover audio", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_upload_and_cover_audio" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/upload-and-extend-audio": { "post": { "operationId": "create_music_upload_and_extend_audio", "summary": "Suno: upload and extend audio", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_upload_and_extend_audio" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/add-instrumental": { "post": { "operationId": "create_music_add_instrumental", "summary": "Suno: add instrumental", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_add_instrumental" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/add-vocals": { "post": { "operationId": "create_music_add_vocals", "summary": "Suno: add vocals", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_add_vocals" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/time-stamped-lyrics": { "post": { "operationId": "create_music_time_stamped_lyrics", "summary": "Suno: time stamped lyrics", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_time_stamped_lyrics" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/boost-music-style": { "post": { "operationId": "create_music_boost_music_style", "summary": "Suno: boost music style", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_boost_music_style" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/cover-generate": { "post": { "operationId": "create_music_cover_generate", "summary": "Suno: cover generate", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_cover_generate" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/replace-section": { "post": { "operationId": "create_music_replace_section", "summary": "Suno: replace section", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_replace_section" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/generate-persona": { "post": { "operationId": "create_music_generate_persona", "summary": "Suno: generate persona", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_generate_persona" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/mashup": { "post": { "operationId": "create_music_mashup", "summary": "Suno: mashup", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_mashup" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/suno-recovery-audio": { "post": { "operationId": "create_music_suno_recovery_audio", "summary": "Suno: suno recovery audio", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_suno_recovery_audio" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/convert-to-wav-format": { "post": { "operationId": "create_music_convert_to_wav_format", "summary": "Suno: convert to wav format", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_convert_to_wav_format" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/create-music-video": { "post": { "operationId": "create_music_create_music_video", "summary": "Suno: create music video", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_create_music_video" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/generate-lyrics": { "post": { "operationId": "create_music_generate_lyrics", "summary": "Suno: generate lyrics", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_generate_lyrics" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/validation-phrase": { "post": { "operationId": "create_music_validation_phrase", "summary": "Suno: validation phrase", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_validation_phrase" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/create-voice": { "post": { "operationId": "create_music_create_voice", "summary": "Suno: create voice", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_create_voice" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/regenerate-phrase": { "post": { "operationId": "create_music_regenerate_phrase", "summary": "Suno: regenerate phrase", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_regenerate_phrase" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/check-voice": { "post": { "operationId": "create_music_check_voice", "summary": "Suno: check voice", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_check_voice" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/separate-vocals": { "post": { "operationId": "create_music_separate_vocals", "summary": "Suno: separate vocals", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_separate_vocals" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/generate-midi-from-audio": { "post": { "operationId": "create_music_generate_midi_from_audio", "summary": "Suno: generate midi from audio", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_generate_midi_from_audio" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } }, "/music/sounds": { "post": { "operationId": "create_music_sounds", "summary": "Suno: sounds", "description": "JSON-объект. Действуют ограничения выбранной модели и серверного окружения. Параметры допускаются только совместимым проверенным маршрутом; неподдержанные поля не отбрасываются. Повтор с прежним Idempotency-Key возвращает существующую операцию. После очистки её содержимого возвращается 410.", "parameters": [ { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "minLength": 1, "maxLength": 191, "pattern": "^[A-Za-z0-9._:\\-]+$" }, "description": "Область ключа — клиент. Изменение параметров с прежним ключом даёт 409." }, { "name": "Prefer", "in": "header", "schema": { "type": "string" }, "description": "respond-async: вернуть собственный ID без ожидания поставщика, в том числе при stream=true. Формат тела и Idempotency-Key сохраняются. Готовый повтор возвращает сохранённый результат." } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SunoInput_music_sounds" } } } }, "responses": { "202": { "description": "Операция принята. Опросите GET /requests/{id}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Request" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } }, "Location": { "schema": { "type": "string" }, "description": "Путь GET /api/v1/requests/{id}." }, "Preference-Applied": { "schema": { "type": "string", "const": "respond-async" } } } }, "502": { "description": "Генерация завершилась ошибкой. Проверьте billing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerationError" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } }, "default": { "description": "Ошибка валидации, настроек, доступа или баланса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }, "headers": { "X-Generation-ID": { "schema": { "$ref": "#/components/schemas/PublicId" } } } } } } } }, "components": { "securitySchemes": { "ClientKey": { "type": "http", "scheme": "bearer", "description": "API-ключ клиента Formula AI." } }, "schemas": { "PublicId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "Billing": { "type": "object", "properties": { "currency": { "const": "RUB" }, "charged": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "refunded": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{7}$", "description": "Рубли, точная десятичная строка с 7 знаками." }, "status": { "enum": [ "pending", "confirmed" ] } }, "required": [ "currency", "charged", "refunded", "status" ] }, "Usage": { "type": "object", "description": "Применимые единицы: prompt_tokens, completion_tokens, total_tokens, input_tokens, output_tokens, token_details, images, videos, audio_seconds, duration_seconds. Счётчики могут быть точными численными строками. Поля себестоимости не выдаются." }, "ErrorDetail": { "type": "object", "properties": { "message": { "type": "string" }, "code": { "type": "string" }, "type": { "type": "string" }, "param": { "type": [ "string", "null" ] } }, "required": [ "message", "code" ] }, "Error": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" } }, "required": [ "error", "request_id" ] }, "GenerationError": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" }, "request_id": { "$ref": "#/components/schemas/PublicId" }, "billing": { "$ref": "#/components/schemas/Billing" } }, "required": [ "error", "request_id", "billing" ] }, "Request": { "type": "object", "properties": { "id": { "$ref": "#/components/schemas/PublicId" }, "object": { "const": "ai_service.request" }, "model": { "type": "string" }, "operation": { "type": "string" }, "status": { "enum": [ "queued", "running", "waiting", "saving_media", "uncertain", "completed", "failed", "cancelled" ] }, "created_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "finished_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expires_at": { "type": [ "string", "null" ], "description": "Время сервиса Europe/Moscow: YYYY-MM-DD HH:mm:ss, возможно дробная часть секунд." }, "content_expired": { "type": "boolean" }, "usage": { "$ref": "#/components/schemas/Usage" }, "billing": { "$ref": "#/components/schemas/Billing" }, "result": { "type": [ "object", "null" ], "description": "Результат выбранной операции. Файлы — только адреса Formula AI либо запрошенный base64." }, "error": { "anyOf": [ { "$ref": "#/components/schemas/ErrorDetail" }, { "type": "null" } ] } }, "required": [ "id", "object", "model", "operation", "status", "created_at", "finished_at", "content_expires_at", "content_expired", "usage", "billing", "result", "error" ] }, "MusicInput": { "type": "object", "properties": { "prompt": { "type": "string", "description": "A description of the desired audio content. Optional.\n- Custom mode (`custom_mode=true`): When provided, the prompt is used strictly as lyrics and sung in the generated track. Character limits by model:\n - V4: Maximum 3000 characters\n - V4_5, V4_5PLUS, V4_5ALL, V5_5, V5: Maximum 5000 characters\n- Non-custom mode (`custom_mode=false`): When provided, the prompt serves as the core idea. Lyrics are generated automatically based on it and do not strictly match the input. Maximum 3000 characters.\n`prompt` is not required. In non-custom mode, provide at least one of `image_urls`, `video_urls`, `audio_urls`, `style`, or `lyrics`. In custom mode, provide at least one of `style`, `lyrics`, or `negative_tags`. Generation is rejected if all related inputs are empty." }, "style": { "type": "string", "description": "Music style specification for the generated audio.\n- Character limits by model:\n - **V4** (Discontinued): Maximum 200 characters\n - **V4_5 and V4_5PLUS** (Discontinued): Maximum 1000 characters\n - **V4_5ALL** (Discontinued): Maximum 1000 characters\n - **V5_5 and V5** (Discontinued): Maximum 1000 characters\n - **V6, V6_MINI, and V6_WILD**: Maximum 1000 characters\n- Common examples: Jazz, Classical, Electronic, Pop, Rock, Hip-hop, etc.", "maxLength": 1000 }, "title": { "type": "string", "description": "Title for the generated music track.\n- Required in Custom Mode (`custom_mode: true`).\n- Max length: 80 characters.\n- Will be displayed in player interfaces and filenames.", "maxLength": 80 }, "custom_mode": { "type": "boolean", "description": "Determines whether advanced parameter customization is enabled.\n- If `true`: Allows detailed control\n- If `false`: Simplified mode" }, "instrumental": { "type": "boolean", "description": "Determines whether the audio should be instrumental (no lyrics).\n- In Custom Mode (`custom_mode: true`):\n - If `true`: Only `style` and `title` are required.\n - If `false`: `style`, `title`, and `prompt` are required (`prompt` is used as the exact lyrics).\n- In Non-custom Mode (`custom_mode: false`):\n - If `true`: Only `style` and `prompt` are required.\n - If `false`: `style`, `prompt`, and `lyrics` are required (`lyrics` is used as a lyrics attachment and can be used together with the audio description in `prompt`)." }, "negative_tags": { "type": "string", "description": "Music styles or traits to exclude from the generated audio. Optional. Use to avoid specific styles." }, "vocal_gender": { "type": "string", "description": "Vocal gender preference. Optional. Use `m` for male and `f` for female. This parameter is only effective when `custom_mode` is true. In practice, it only increases probability and cannot guarantee male/female voice instructions are followed.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Strength of adherence to the specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Controls experimental/creative deviation. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Relative weight of audio features. Optional. Range 0–1, up to 2 decimal places.\n- `audio_weight` is not supported when there are no vocals", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "style_persona", "voice_persona" ], "description": "Persona model. Only available for V5 (Discontinued), V5.5 (Discontinued), V6, V6_MINI, and V6_WILD." }, "duration": { "type": "number", "default": 20, "minimum": 10, "maximum": 360, "description": "Audio duration. Optional; only valid when `custom_mode` is true and the model is V5_5 (Discontinued), V6, V6_MINI, or V6_WILD." }, "image_urls": { "type": "array", "description": "Image references. Only effective when `custom_mode=false`.\n- Optional\n- Up to 5 images; each image must not exceed 10 MB\n- Supported formats: `jpeg`, `png`, `webp`, and `bmp`", "items": { "type": "string" }, "maxItems": 5 }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "multipleOf": 1, "description": "Only available when Custom Mode (`custom_mode: true`) is enabled. Controls the diversity and stylistic variation of generated results. Optional. Range 0–4, integer, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "lyrics": { "type": "string", "description": "Lyrics for the generated audio. Optional.\n- V6: Maximum 5000 characters.\n- When `custom_mode=true`, `lyrics` is used as lyrics first; if `lyrics` is not provided, `prompt` is used as lyrics.\n- When `custom_mode=false`, `lyrics` is used as a lyrics attachment and can be used together with the audio description in `prompt`.", "maxLength": 5000 }, "audio_urls": { "type": "array", "items": { "type": "string" }, "description": "Audio references. Only effective when `custom_mode=false`.\n- Optional\n- Audio duration must be between 6 seconds and 30 minutes; each audio file must not exceed 500 MB", "maxItems": 10 }, "video_urls": { "type": "array", "items": { "type": "string" }, "description": "Video references. Only effective when `custom_mode=false`.\n- Optional\n- Up to 1 video file; each file must not exceed 100 MB, duration must not exceed 241 seconds, with no minimum duration\n- Supported formats: mp4, mov, and webm", "maxItems": 1 }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "custom_mode", "instrumental" ], "additionalProperties": false, "allOf": [ { "if": { "properties": { "custom_mode": { "const": true } }, "required": [ "custom_mode" ] }, "then": { "required": [ "title" ], "properties": { "prompt": { "maxLength": 5000 }, "title": { "minLength": 1 }, "image_urls": { "maxItems": 0 }, "video_urls": { "maxItems": 0 }, "audio_urls": { "maxItems": 0 } }, "anyOf": [ { "required": [ "style" ], "properties": { "style": { "minLength": 1 } } }, { "required": [ "lyrics" ], "properties": { "lyrics": { "minLength": 1 } } }, { "required": [ "negative_tags" ], "properties": { "negative_tags": { "minLength": 1 } } } ] }, "else": { "properties": { "prompt": { "maxLength": 3000 } }, "anyOf": [ { "required": [ "style" ], "properties": { "style": { "minLength": 1 } } }, { "required": [ "lyrics" ], "properties": { "lyrics": { "minLength": 1 } } }, { "required": [ "image_urls" ], "properties": { "image_urls": { "minItems": 1 } } }, { "required": [ "audio_urls" ], "properties": { "audio_urls": { "minItems": 1 } } }, { "required": [ "video_urls" ], "properties": { "video_urls": { "minItems": 1 } } } ], "not": { "anyOf": [ { "required": [ "duration" ] }, { "required": [ "vocal_gender" ] }, { "required": [ "style_weight" ] }, { "required": [ "weirdness_constraint" ] }, { "required": [ "audio_weight" ] }, { "required": [ "variety" ] } ] } } }, { "if": { "properties": { "instrumental": { "const": true } }, "required": [ "instrumental" ] }, "then": { "not": { "required": [ "audio_weight" ] } } } ] }, "SunoInput_music_extend": { "type": "object", "properties": { "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "prompt": { "type": "string", "description": "Lyrics for the extended audio. Optional.\n- Used as lyrics only when `lyrics` is not provided. If `lyrics` is present, `prompt` is not used as lyrics.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`audio_id`) is enough to generate.\n- Do not pass this parameter when `instrumental` is `true`.", "maxLength": 5000 }, "style": { "type": "string", "description": "Music style specification for the extended audio. Optional.\n- Not required. Source audio (`audio_id`) is enough to generate.\n- Character limits by model:\n - **V4**(Discontinued): Maximum 200 characters\n - **V4_5 & V4_5PLUS**(Discontinued): Maximum 1000 characters\n - **V4_5ALL**(Discontinued): Maximum 1000 characters\n - **V5_5 & V5**(Discontinued): Maximum 1000 characters\n - **V6, V6_MINI, and V6_WILD**: Maximum 1000 characters\n- Should typically align with the original audio's style for best results.", "maxLength": 1000 }, "title": { "type": "string", "description": "Title for the extended music track. Optional.\n- Maximum 100 characters.\n- Displayed in player interfaces and filenames.", "maxLength": 100 }, "continue_at": { "type": "number", "description": "The time point (in seconds) from which to start extending the music.\n- Value range: greater than 0 and less than the total duration of the generated audio.\n- Specifies the position in the original track where the extension should begin." }, "negative_tags": { "type": "string", "description": "Music styles or traits to exclude from the extended audio. Optional. Use to avoid specific undesired characteristics." }, "vocal_gender": { "type": "string", "description": "Vocal gender preference. Optional. 'm' denotes a male voice, and 'f' denotes a female voice. In practice, this parameter only influences the probability and does not guarantee strict adherence to the gender instruction. This parameter is not required if `instrumental` is set to `true`.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Strength of adherence to the specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Balance weight for audio features vs. other factors. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "style_persona", "voice_persona" ], "description": "The persona model is only applicable to the V5 (deactivated), V5.5 (deactivated), V6, V6_MINI, and V6_WILD models." }, "instrumental": { "type": "boolean", "default": false, "description": "Indicates whether it is an instrumental track. If true, passing the `lyrics`, `prompt`, and `vocal_gender` parameters is prohibited." }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the extended audio. Optional.\n- Takes priority over `prompt` as lyrics. If omitted, `prompt` is used as lyrics when provided.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`audio_id`) is enough to generate.\n- Do not pass this parameter when `instrumental` is `true`." }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "audio_id" ], "additionalProperties": false, "allOf": [ { "if": { "properties": { "instrumental": { "const": true } }, "required": [ "instrumental" ] }, "then": { "not": { "anyOf": [ { "required": [ "prompt" ] }, { "required": [ "lyrics" ] }, { "required": [ "vocal_gender" ] } ] } } } ] }, "SunoInput_music_upload_and_cover_audio": { "type": "object", "properties": { "upload_url": { "type": "string", "format": "uri", "description": "The URL of the source audio file to cover. Required. Source audio (`upload_url`) is enough to generate. Ensure the uploaded audio does not exceed 8 minutes in length." }, "prompt": { "type": "string", "description": "Lyrics for the covered audio. Optional.\n- Used as lyrics only when `lyrics` is not provided. If `lyrics` is present, `prompt` is not used as lyrics.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`upload_url`) is enough to generate.\n- Do not pass this parameter when `instrumental` is `true`.", "maxLength": 5000 }, "style": { "type": "string", "description": "The music style or genre for the audio. Optional.\n- Not required. Source audio (`upload_url`) is enough to generate.\n- Character limits by model:\n - **V4**(Discontinued): Maximum 200 characters\n - **V4_5 & V4_5PLUS**(Discontinued): Maximum 1000 characters\n - **V4_5ALL**(Discontinued): Maximum 1000 characters\n - **V5_5 & V5**(Discontinued): Maximum 1000 characters\n - **V6, V6_MINI, and V6_WILD**: Maximum 1000 characters", "maxLength": 1000 }, "title": { "type": "string", "description": "The title of the generated music track. Optional.\n- Maximum 80 characters.\n- Displayed in player interfaces and filenames.", "maxLength": 80 }, "instrumental": { "type": "boolean", "description": "Determines if the audio should be instrumental (no lyrics).\n- If `true`: generate instrumental music (no vocals). Do not pass `lyrics`, `prompt`, or `vocal_gender`.\n- If `false`: use `lyrics` as lyrics (fall back to `prompt` if `lyrics` is not provided)." }, "negative_tags": { "type": "string", "description": "Music styles or traits to exclude from the generated audio. \n- Optional. Use to avoid specific styles. \n Example: \"Heavy Metal, Upbeat Drums\"" }, "vocal_gender": { "type": "string", "description": "Vocal gender preference for the singing voice. Optional. Use 'm' for male and 'f' for female. Based on practice, this parameter can only increase the probability but cannot guarantee adherence to male/female voice instructions. Do not pass this parameter when `instrumental` is `true`.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Strength of adherence to the specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Balance weight for audio features vs. other factors. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "voice_persona", "style_persona" ], "description": "The persona model is only available for models version 5 and 5.5." }, "duration": { "type": "integer", "default": 20, "minimum": 10, "maximum": 360, "description": "Audio duration in seconds. Optional. Range 10–360, default 20. Valid when `model` is ~~V5_5~~ (Discontinued), V6, V6_MINI, or V6_WILD." }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the covered audio. Optional.\n- Takes priority over `prompt` as lyrics. If omitted, `prompt` is used as lyrics when provided.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`upload_url`) is enough to generate.\n- Do not pass this parameter when `instrumental` is `true`." }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "upload_url", "instrumental" ], "additionalProperties": false, "allOf": [ { "if": { "properties": { "instrumental": { "const": true } }, "required": [ "instrumental" ] }, "then": { "not": { "anyOf": [ { "required": [ "prompt" ] }, { "required": [ "lyrics" ] }, { "required": [ "vocal_gender" ] } ] } } } ] }, "SunoInput_music_upload_and_extend_audio": { "type": "object", "properties": { "upload_url": { "type": "string", "format": "uri", "description": "The URL of the source audio file to extend. Required. Source audio (`upload_url`) is enough to generate. Ensure the uploaded audio does not exceed 8 minutes in length." }, "instrumental": { "type": "boolean", "description": "Determines whether the audio is instrumental (without lyrics). Optional; defaults to `false`.\n- If `true`: generate instrumental music (no vocals). Do not pass `lyrics`, `prompt`, or `vocal_gender`.\n- If `false`: use `lyrics` as lyrics (fall back to `prompt` if `lyrics` is not provided).", "default": false }, "prompt": { "type": "string", "description": "Lyrics for the extended audio. Optional.\n- Used as lyrics only when `lyrics` is not provided. If `lyrics` is present, `prompt` is not used as lyrics.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`upload_url`) is enough to generate.\n- Do not pass this parameter when `instrumental` is `true`.", "maxLength": 5000 }, "style": { "type": "string", "description": "Music style, e.g., Jazz, Classical, Electronic. Optional.\n- Not required. Source audio (`upload_url`) is enough to generate.\n- Character limits by model:\n- **V6, V6_MINI & V6_WILD**: Maximum 1000 characters\n- **V5_5 & V5** (Discontinued): Maximum 1000 characters\n- **V4_5PLUS & V4_5** (Discontinued): Maximum 1000 characters\n- **V4_5ALL** (Discontinued): Maximum 1000 characters\n- **V4** (Discontinued): Maximum 200 characters", "maxLength": 1000 }, "title": { "type": "string", "description": "Music title. Optional.\n- Maximum 100 characters.\n- Displayed in player interfaces and filenames.", "maxLength": 100 }, "continue_at": { "type": "number", "description": "The time point (in seconds) from which to start extending the music.\n- Value range: greater than 0 and less than the total duration of the uploaded audio.\n- Specifies the position in the original track where the extension should begin." }, "negative_tags": { "type": "string", "description": "Music styles to exclude from generation" }, "vocal_gender": { "type": "string", "description": "Vocal gender preference for the singing voice. Optional. Use 'm' for male and 'f' for female. Based on practice, this parameter can only increase the probability but cannot guarantee adherence to male/female voice instructions. Do not pass this parameter when `instrumental` is `true`.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Strength of adherence to the specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Balance weight for audio features vs. other factors. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "style_persona", "voice_persona" ], "description": "The persona model is only available for models version V5 (Discontinued), V5_5 (Discontinued), V6, V6_MINI, and V6_WILD." }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the extended audio. Optional.\n- Takes priority over `prompt` as lyrics. If omitted, `prompt` is used as lyrics when provided.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`upload_url`) is enough to generate.\n- Do not pass this parameter when `instrumental` is `true`." }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "upload_url" ], "additionalProperties": false, "allOf": [ { "if": { "properties": { "instrumental": { "const": true } }, "required": [ "instrumental" ] }, "then": { "not": { "anyOf": [ { "required": [ "prompt" ] }, { "required": [ "lyrics" ] }, { "required": [ "vocal_gender" ] } ] } } } ] }, "SunoInput_music_add_instrumental": { "type": "object", "properties": { "upload_url": { "type": "string", "format": "uri", "description": "URL of the uploaded audio file. Required. Source audio (`upload_url`) is enough to generate once `title`, `negative_tags`, and `tags` are provided." }, "title": { "type": "string", "description": "Title of the generated music. Required. Will be displayed in the player interface and file name." }, "negative_tags": { "type": "string", "description": "Music styles or characteristics to exclude from the generated audio. Required. Used to avoid specific unwanted music elements.", "maxLength": 200 }, "tags": { "type": "string", "description": "Music styles or tags to include in the generated music. Required. Defines the desired music style and characteristics.", "maxLength": 1000 }, "vocal_gender": { "type": "string", "description": "Vocal gender preference. Optional. 'm' for male, 'f' for female. Based on practice, this parameter can only increase the probability but cannot guarantee adherence to male/female voice instructions.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Adherence strength to specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation level. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Relative weight of audio elements. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the generated audio. Optional.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`upload_url`) is enough to generate once `title`, `negative_tags`, and `tags` are provided." }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "upload_url", "title", "negative_tags", "tags" ], "additionalProperties": false }, "SunoInput_music_add_vocals": { "type": "object", "properties": { "prompt": { "type": "string", "description": "Prompt for generating audio. Optional.\n- Used as lyrics only when `lyrics` is not provided. If `lyrics` is present, `prompt` is not used as lyrics.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.", "maxLength": 5000 }, "title": { "type": "string", "description": "Music title. Required. Will be displayed in the player interface and file name." }, "negative_tags": { "type": "string", "description": "Excluded music styles. Required. Used to avoid including specific styles or elements in the generated music.", "maxLength": 200 }, "style": { "type": "string", "description": "Music style. Required. Such as jazz, electronic, classical and other music types.", "maxLength": 1000 }, "vocal_gender": { "type": "string", "description": "Vocal gender preference. Optional. 'm' for male, 'f' for female. Based on practice, this parameter can only increase the probability but cannot guarantee adherence to male/female voice instructions.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Adherence strength to specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation level. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Relative weight of audio elements. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "upload_url": { "type": "string", "format": "uri", "description": "URL of the uploaded audio file. Required. Source audio (`upload_url`) is enough to generate once `title`, `negative_tags`, and `style` are provided." }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the generated audio. Optional.\n- Takes priority over `prompt` as lyrics. If omitted, `prompt` is used as lyrics when provided.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.\n- Not required. Source audio (`upload_url`) is enough to generate once `title`, `negative_tags`, and `style` are provided." }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "upload_url", "title", "negative_tags", "style" ], "additionalProperties": false }, "SunoInput_music_time_stamped_lyrics": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id", "audio_id" ], "additionalProperties": false }, "SunoInput_music_boost_music_style": { "type": "object", "properties": { "content": { "type": "string", "description": "Style description. Please describe in concise and clear language the music style you expect to generate. Example: 'Pop, Mysterious'" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "content" ], "additionalProperties": false }, "SunoInput_music_cover_generate": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id" ], "additionalProperties": false }, "SunoInput_music_replace_section": { "type": "object", "properties": { "prompt": { "type": "string", "description": "The lyrics after replacement. Required." }, "tags": { "type": "string", "description": "Style tags for the music, such as jazz or electronic. Required." }, "title": { "type": "string", "description": "The title of the music. Required." }, "negative_tags": { "type": "string", "description": "Music styles to exclude, used to prevent specific stylistic elements from appearing in the replacement section." }, "infill_start_s": { "type": "number", "description": "The point in time, in seconds, at which replacement starts, rounded to 2 decimal places. Required. Must be less than infill_end_s, and the difference between infill_end_s and infill_start_s must be at least 10 seconds.", "minimum": 0 }, "infill_end_s": { "type": "number", "description": "The point in time, in seconds, at which replacement ends, rounded to 2 decimal places. Required. Must be greater than infill_start_s, and the difference between infill_end_s and infill_start_s must be at least 10 seconds.", "minimum": 0 }, "full_lyrics": { "type": "string", "description": "The complete modified lyrics, combining the modified and unmodified lyrics. Required. This parameter contains the complete lyrics text that will be used for the entire song after section replacement." }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the replaced segment. Optional.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters." }, "vocal_gender": { "type": "string", "description": "Vocal gender preference. Optional. 'm' for male, 'f' for female. Based on practice, this parameter can only increase the probability but cannot guarantee adherence to male/female voice instructions.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Adherence strength to specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation level. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Relative weight of audio elements. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "style_persona", "voice_persona" ], "default": "style_persona", "description": "Persona model. Optional. Use `style_persona` for Generate Persona IDs, or `voice_persona` for Suno Voice IDs. Default: `style_persona`. Available for V5 (Discontinued), V5_5 (Discontinued), V6, V6_MINI, and V6_WILD." }, "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "upload_url": { "type": "string", "format": "uri", "description": "The URL of the custom audio uploaded by the user." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "prompt", "tags", "title", "infill_start_s", "infill_end_s", "full_lyrics" ], "additionalProperties": false, "oneOf": [ { "required": [ "task_id", "audio_id" ], "not": { "anyOf": [ { "required": [ "upload_url" ] } ] }, "type": "object" }, { "required": [ "upload_url" ], "not": { "anyOf": [ { "required": [ "task_id" ] }, { "required": [ "audio_id" ] } ] }, "type": "object" } ] }, "SunoInput_music_generate_persona": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "name": { "type": "string", "description": "Name for the Persona. A descriptive name that captures the essence of the musical style or character." }, "description": { "type": "string", "description": "Detailed description of the Persona's musical characteristics, style, and personality. Be specific about genre, mood, instrumentation, and vocal qualities." }, "vocal_start": { "type": "number", "description": "Start time (in seconds) for Persona analysis segment extraction. Used to specify the time point in the audio from which to extract the segment for Persona analysis. Must be less than vocal_end, and vocal_end - vocalStart must be between 10–30 seconds. Defaults to 0.0.", "default": 0, "minimum": 0 }, "vocal_end": { "type": "number", "description": "End time (in seconds) for Persona analysis segment extraction. Together with vocal_start, used to specify the time range for analysis. vocalEnd - vocalStart must be between 10–30 seconds. Defaults to 30.0.", "multipleOf": 0.01, "default": 30, "minimum": 0 }, "style": { "type": "string", "description": "Optional. Used to supplement the description of the music style tag corresponding to the Persona, such as \"Electronic Pop\", \"Jazz Trio\", etc." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id", "audio_id", "name", "description" ], "additionalProperties": false }, "SunoInput_music_mashup": { "type": "object", "properties": { "upload_url_list": { "type": "array", "description": "Array of audio file URLs to mashup. Provide 2 audio URLs. Upstream uses only the first 2. Each URL must be publicly accessible.", "items": { "type": "string", "format": "uri" }, "minItems": 2, "maxItems": 2 }, "prompt": { "type": "string", "description": "A description of the desired audio content. Optional.\n- Used as lyrics only when `lyrics` is not provided. If `lyrics` is present, `prompt` is not used as lyrics.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters.", "maxLength": 5000 }, "style": { "type": "string", "description": "Music style specification for the generated audio. Required.\n- Character limits by model:\n - **V4** (Discontinued): Maximum 200 characters\n - **V4_5 & V4_5PLUS** (Discontinued): Maximum 1000 characters\n - **V4_5ALL** (Discontinued): Maximum 1000 characters\n - **V5 & V5_5** (Discontinued): Maximum 1000 characters\n - **V6, V6_MINI & V6_WILD**: Maximum 1000 characters\n- Common examples: Jazz, Classical, Electronic, Pop, Rock, Hip-hop, etc.", "maxLength": 1000 }, "title": { "type": "string", "description": "Title for the generated music track. Required.\n- Character limits by model:\n - **V4** (Discontinued): Maximum 80 characters\n - **V4_5 & V4_5PLUS** (Discontinued): Maximum 100 characters\n - **V4_5ALL** (Discontinued): Maximum 80 characters\n - **V5 & V5_5** (Discontinued): Maximum 100 characters\n - **V6, V6_MINI & V6_WILD**: Maximum 100 characters\n- Will be displayed in player interfaces and filenames.", "maxLength": 100 }, "vocal_gender": { "type": "string", "description": "Vocal gender preference for the singing voice. Optional. Use 'm' for male and 'f' for female. Based on practice, this parameter can only increase the probability but cannot guarantee adherence to male/female voice instructions.", "enum": [ "m", "f" ] }, "style_weight": { "type": "number", "description": "Strength of adherence to the specified style. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "weirdness_constraint": { "type": "number", "description": "Controls experimental/creative deviation. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "audio_weight": { "type": "number", "description": "Balance weight for audio features vs. other factors. Optional. Range 0–1, up to 2 decimal places.", "minimum": 0, "maximum": 1, "multipleOf": 0.01 }, "duration": { "type": "integer", "default": 20, "minimum": 10, "maximum": 360, "description": "Audio duration in seconds. Optional. Range 10–360, default 20. Valid when `model` is ~~V5_5~~ (Discontinued), V6, V6_MINI, or V6_WILD." }, "lyrics": { "type": "string", "maxLength": 5000, "description": "Lyrics for the mashup. Optional.\n- Takes priority over `prompt` as lyrics. If omitted, `prompt` is used as lyrics when provided.\n- V6, V6_MINI, and V6_WILD: Maximum 5000 characters." }, "variety": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "description": "Controls the diversity and stylistic variation of generated results. Optional. Integer range 0–4, default 1.\n\n- **`0`**: off (exact style) — fully off, strictly the same style\n- **`1`**: normal (balanced variety) — default, balances stability and diversity\n- **`2`**: high (distinct styles) — produces results with clearly different styles\n- **`3`**: extra (bold exploration) — higher variation, encourages bold exploration of different styles\n- **`4`**: max (unreasonably varied) — maximum diversity; results may differ in style very significantly" }, "persona_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "persona_model": { "type": "string", "enum": [ "style_persona", "voice_persona" ], "default": "style_persona", "description": "Persona model. Optional. Use `style_persona` for Generate Persona IDs, or `voice_persona` for Suno Voice IDs. Default: `style_persona`. Available for V5 (Discontinued), V5_5 (Discontinued), V6, V6_MINI, and V6_WILD." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "upload_url_list", "style", "title" ], "additionalProperties": false }, "SunoInput_music_suno_recovery_audio": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id" ], "additionalProperties": false }, "SunoInput_music_convert_to_wav_format": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id", "audio_id" ], "additionalProperties": false }, "SunoInput_music_create_music_video": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "author": { "type": "string", "maxLength": 50, "description": "Artist or creator name to display as a signature on the video cover. Maximum 50 characters. This creates attribution for the music creator." }, "domain_name": { "type": "string", "maxLength": 50, "description": "Website or brand to display as a watermark at the bottom of the video. Maximum 50 characters. Useful for promotional branding or attribution." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id", "audio_id" ], "additionalProperties": false }, "SunoInput_music_generate_lyrics": { "type": "object", "properties": { "prompt": { "type": "string", "description": "Description of the desired lyrics content. Be specific about theme, mood, style, or story elements you want in the lyrics. More detailed prompts yield better results. The maximum word limit is 200 characters." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "prompt" ], "additionalProperties": false }, "SunoInput_music_validation_phrase": { "type": "object", "properties": { "voice_url": { "description": "The original recording URL uploaded by the user [Required]", "type": "string" }, "vocal_start_s": { "description": "Start time (in seconds) for extracting the vocal segment [Required]", "type": "integer" }, "vocal_end_s": { "description": "End time (in seconds) for extracting the vocal segment, must be greater than vocal_start_s [Required]", "type": "integer" }, "language": { "description": "Verify the language of the short phrase. Supported languages ​​include: English (en), Chinese (zh), Spanish (es), French (fr), Portuguese (pt), German (de), Japanese (ja), Korean (ko), Hindi (hi), and Russian (ru).", "type": "string" }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "voice_url", "vocal_start_s", "vocal_end_s" ], "additionalProperties": false }, "SunoInput_music_create_voice": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "verify_url": { "description": "Audio URL for the user's recording of the server-returned `validateInfo` validation phrase. For best voice generation results, recording the phrase in a singing voice rather than plain speech is recommended. [Required]", "type": "string" }, "voice_name": { "description": "Voice name", "type": "string" }, "description": { "description": "Voice description", "type": "string" }, "style": { "description": "Voice style", "type": "string" }, "singer_skill_level": { "type": "string", "description": "Singer skill level. Supported: beginner, intermediate, advanced, professional", "default": "beginner", "enum": [ "beginner", "intermediate", "advanced", "professional" ] }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id", "verify_url" ], "additionalProperties": false }, "SunoInput_music_regenerate_phrase": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id" ], "additionalProperties": false }, "SunoInput_music_check_voice": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id" ], "additionalProperties": false }, "SunoInput_music_separate_vocals": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "separate_vocal", "split_stem", "split_stem_advanced" ], "default": "separate_vocal", "description": "Separation type, options:\n\n- **separate_vocal**: Separate vocals and accompaniment, generating a vocal track and an instrumental track.\n- **split_stem**: Separate various instrument sounds, generating multiple instrument tracks.\n- **split_stem_advanced**: Advanced multi-track separation, supporting the specification of a particular instrument track to generate finer instrument tracks." }, "stem_name": { "type": "string", "enum": [ "Lead Vocal", "Drum Kit", "Kick", "Snare", "Risers", "Bass", "Backing Vocals", "Piano", "Electric Guitar", "Percussion", "String Section", "Synth", "Acoustic Guitar", "Sound Effects", "Synth Pad", "Synth Bass", "Guitar", "Brass Section", "Organ", "Electronic Drum Kit", "Lead Electric Guitar", "Synth Keys", "Rhythm Electric Guitar", "Electric Piano", "Upright Bass", "Keyboards", "Distorted Electric Guitar", "Synth Strings", "Synth Lead", "Woodwinds", "Rhythm Acoustic Guitar", "Flute", "Harp", "Tambourine", "Trumpet", "Arpeggiator", "Accordion", "Fiddle", "Pedal Steel Guitar", "Synth Voice", "Violin", "Digital Piano", "Synth Brass", "Mandolin", "Choir", "Banjo", "Bells", "Clarinet", "Tenor Saxophone", "Trombone", "Shaker", "French Horn", "Glockenspiel", "Electric Bass", "Cello", "Timpani", "Harmonica", "Marimba", "Vibraphone", "Lap Steel Guitar", "Saxophone", "Orchestra", "Horns", "Cymbals", "Hand Clap", "Oboe", "Celesta", "Congas", "Drone", "Alto Saxophone", "Double Bass", "Ukulele", "Harpsichord", "Baritone Saxophone", "Xylophone", "Tuba", "Bass Guitar", "Whistle", "Lead Guitar", "Rhodes", "808", "Bongos", "Bassoon", "Cowbell", "Viola", "Sitar", "Steel Drums", "Piccolo", "Theremin", "Bagpipes", "Hi-Hat", "Music Box", "Melodica", "Tabla", "Koto", "Djembe", "Taiko", "Didgeridoo" ], "description": "Only used when `type` is `split_stem_advanced` to request a specific stem track/instrument name." }, "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "audio_url": { "type": "string", "format": "uri", "description": "Custom audio URL uploaded by the user. The maximum file size is 20MB." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model" ], "additionalProperties": false, "oneOf": [ { "required": [ "task_id", "audio_id" ], "not": { "anyOf": [ { "required": [ "audio_url" ] } ] }, "type": "object" }, { "required": [ "audio_url" ], "not": { "anyOf": [ { "required": [ "task_id" ] }, { "required": [ "audio_id" ] } ] }, "type": "object" } ], "allOf": [ { "if": { "properties": { "type": { "const": "split_stem_advanced" } }, "required": [ "type" ] }, "then": { "required": [ "stem_name" ] }, "else": { "not": { "required": [ "stem_name" ] } } } ] }, "SunoInput_music_generate_midi_from_audio": { "type": "object", "properties": { "task_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного исходного запроса." }, "audio_id": { "type": "string", "pattern": "^[a-f0-9]{32}$", "description": "ID собственного файла трека." }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "task_id" ], "additionalProperties": false }, "SunoInput_music_sounds": { "type": "object", "properties": { "prompt": { "description": "Sound task type limit: 500 characters", "type": "string" }, "sound_loop": { "type": "boolean", "description": "Is it a cycle?", "default": false }, "sound_tempo": { "type": "integer", "description": "Do not broadcast.", "default": null, "minimum": 1, "maximum": 300 }, "sound_key": { "type": "string", "default": "Any", "enum": [ "Cm", "C#m", "Dm", "D#m", "Em", "Fm", "F#m", "Gm", "G#m", "Am", "A#m", "Bm", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ] }, "grab_lyrics": { "type": "boolean", "description": "Whether to capture the lyrics subtitles \n\nWill the interface be called after completion to obtain the lyrics subtitles?", "default": false }, "model": { "type": "string", "minLength": 1, "description": "Публичный ID Suno из GET /models." } }, "required": [ "model", "prompt" ], "additionalProperties": false } } }, "externalDocs": { "url": "https://formula-ai.ru/docs" } } ``` Полный машинный справочник: https://formula-ai.ru/openapi.json