Разработчикам

Salven API

Модели студии по ценам студии: из Claude, из вашего кода или из AI-агента. Каждый запуск оплачивается вашими кредитами, а результат попадает в вашу галерею.

Обзор

Salven API делает то же, что студия Salven: те же модели, те же цены, те же проверки. Через него создаются изображения, видео, аудио (речь, музыка, звуки) и 3D-модели, применяются готовые эффекты, а сохранённые персонажи попадают в изображение или ролик по своему @handle.

Запуск оплачивается кредитами аккаунта, которому принадлежит ключ, а результат попадает в галерею этого аккаунта на salven.ai. Цена известна заранее, до любых списаний (пробный запрос); кредиты списываются при старте, а за неудавшийся запуск возвращаются.

API открыт для каждого аккаунта Salven. Пользоваться им можно тремя способами:

СпособДля когоЧто понадобится
ClaudeДля всех, кто пользуется Claude: вы просите в чате, Claude запускает модели.Адрес https://salven.ai/mcp и вход в ваш аккаунт Salven.
RESTДля ваших скриптов, сервисов и автоматизаций.API-ключ и любой HTTP-клиент.
АгентДля Claude Code, Cursor и других агентов, которые пишут код.API-ключ и файл SKILL.md.

Все три способа приводят к одному и тому же серверному коду, поэтому запуск стоит одинаково и проходит одни и те же проверки, каким бы путём он ни начался.

Подключить Claude

Salven работает как удалённый MCP-сервер по адресу https://salven.ai/mcp. Добавьте его в Claude один раз, и Claude сможет выбрать модель, назвать цену, запустить генерацию и отдать вам результат — всё от имени вашего аккаунта Salven.

Claude в браузере, на компьютере и в телефоне

  1. В Claude откройте Customize → Connectors → Add custom connector. В мобильных приложениях добавить коннектор нельзя: сделайте этот шаг на claude.ai, и после этого коннектор работает и в мобильных приложениях.
  2. Вставьте адрес https://salven.ai/mcp.
  3. Claude отправит вас на Salven. Войдите, если ещё не вошли, прочитайте, что сможет приложение, и нажмите Разрешить.

Кто может добавить коннектор, зависит от вашего тарифа Claude: на тарифе Free можно добавить один свой коннектор, а на тарифах Team и Enterprise коннектор добавляет для всей организации её владелец (или участник, роль которого это разрешает), а не каждый участник для себя.

Тот же адрес с кнопкой копирования есть в Настройках, в блоке Claude и API.

Claude Code

bash
# Add Salven to Claude Code as an MCP server. Then, inside Claude Code, run /mcp and pick salven to sign in.
claude mcp add --transport http salven https://salven.ai/mcp

Команда только сохраняет сервер. Чтобы войти, запустите Claude Code, выполните /mcp, выберите salven и начните вход: в браузере откроется Salven, где нужно войти в аккаунт и нажать Разрешить, как описано выше.

Другие MCP-клиенты

Тем же адресом может пользоваться любой клиент, который работает с MCP по Streamable HTTP. Клиент с поддержкой OAuth выполнит вход так же, как Claude. Клиент, который отправляет фиксированный заголовок, может вместо этого использовать API-ключ:

Server URL:  https://salven.ai/mcp
Transport:   Streamable HTTP
Header:      Authorization: Bearer <your Salven API key>

В Claude Code тот же заголовок задаётся при добавлении сервера, вместо входа, описанного выше; ключ оболочка берёт из переменной окружения SALVEN_API_KEY:

bash
# Add Salven to Claude Code with an API key instead of the OAuth sign-in.
# The shell reads the key from SALVEN_API_KEY, so it is never typed into the command.
# Claude Code saves the header, with the key in it, together with the server.
claude mcp add --transport http salven https://salven.ai/mcp \
  --header "Authorization: Bearer $SALVEN_API_KEY"

Что умеет Claude

Подключение даёт Claude инструменты из таблицы ниже. Они вызывают тот же код, что и REST API, а запуски через них учитываются в тех же лимитах.

ИнструментЧто делает
accountАккаунт, от имени которого работает подключение, и его баланс кредитов.
list_modelsДоступные сейчас модели, при желании только одного вида: ID, режимы, входы и базовая цена.
get_modelПолная спецификация одной модели: режимы, параметры, входы, варианты соотношения сторон, ограничения на промпт и цены.
generateЗапускает генерацию на выбранной модели или только называет цену при dryRun: true. Ждёт примерно до 45 секунд и возвращает готовые файлы либо ID запусков, которые ещё идут.
get_generationСтатус одной генерации и её файлы, когда она готова. Может подождать завершения до 40 секунд.
list_generationsРаботы аккаунта, сначала новые: созданные и на сайте, и через API.
list_effectsГотовые эффекты: какие изображения нужны каждому, его цена и варианты качества.
run_effectПрименяет эффект к вашему изображению или изображениям либо только называет цену при dryRun: true.
list_charactersВаши персонажи и AI-инфлюенсеры, у каждого свой @handle.
create_characterСохраняет человека как персонажа: имя, @handle и от одного до четырёх фото. Бесплатно.
upload_from_urlКопирует файл с публичного веб-адреса в ваши загрузки на Salven. Salven скачивает файл сам: изображения до 10 МБ, аудио до 20 МБ, видео и модели GLB до 60 МБ.
create_upload_linkСоздаёт одноразовую ссылку, по которой можно добавить файлы с вашего устройства.
get_upload_linkФайлы, которые пришли по ссылке для загрузки.

Как передать Claude файл

Входом модели может быть только ваш собственный файл на Salven, а чат не может передать инструменту картинку, которую вы прикрепили к сообщению. У Claude есть три способа получить файл:

  • Результат одной из ваших прошлых работ: Claude находит его через list_generations.
  • Публичный веб-адрес: Claude копирует файл через upload_from_url. Salven скачивает файл сам, поэтому ограничение прямой загрузки в 25 МБ здесь не действует: изображение может быть до 10 МБ, аудиофайл до 20 МБ, видео или модель GLB до 60 МБ. Это способ передать видео или 3D-модель больше 25 МБ. Скачивание должно завершиться, пока Claude ждёт ответа инструмента, поэтому большому файлу нужен адрес, который отдаёт его быстро.
  • Файл с вашего устройства: Claude создаёт ссылку для загрузки через create_upload_link и даёт вам страницу, которую нужно открыть. Там вы выбираете файлы (входить в аккаунт не нужно) и сообщаете Claude, что они на месте. Ссылка действует 30 минут и принимает до 10 файлов, форматы и размеры те же, что в правилах загрузки: отправленный так файл проходит через веб-сервер, поэтому видео или модель GLB должны быть меньше 25 МБ. Агент с терминалом, например Claude Code, может сам отправить по ссылке файл с диска.

Кредиты и отключение

Всё, что создаёт Claude, оплачивается вашими кредитами по ценам студии. generate и run_effect умеют назвать цену, не запуская генерацию, а инструкции Salven велят Claude назвать стоимость и дождаться вашего согласия перед дорогим запуском (видео, 3D, несколько изображений) и перед тратой, о которой вы не просили. Когда именно спрашивать, всё равно решает Claude, поэтому, если сначала хотите узнать цену, так и скажите.

Чтобы отключить приложение, откройте Настройки, найдите его в блоке Claude и API и нажмите Отключить два раза (второе нажатие подтверждает). Приложение теряет доступ сразу.

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

Одно изображение через REST, от начала до конца. Понадобятся аккаунт Salven с кредитами на балансе и терминал с curl.

  1. Откройте Настройки, найдите блок Claude и API, введите название ключа и нажмите Создать ключ.
  2. Скопируйте ключ сразу: он показывается один раз.
  3. Положите его в переменную окружения SALVEN_API_KEY, например командой export SALVEN_API_KEY=<your key> в той оболочке, где будете работать.
  4. Выполните вызовы ниже по одному.
bash
API=https://salven.ai/api/v1
AUTH="Authorization: Bearer $SALVEN_API_KEY"

# Who the key acts as, and the balance
curl -s "$API/me" -H "$AUTH"

# The image models, each with its params, inputs and pricing
curl -s "$API/models?kind=image" -H "$AUTH"

# Price first: dryRun answers the cost and starts nothing
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"model":"nano-banana-2","prompt":"a red fox in the snow, film photo","params":{"aspect_ratio":"16:9","resolution":"2K"},"dryRun":true}'

# Then run it. The Idempotency-Key names THIS generation: to retry it (a timeout, a 5xx, a 409),
# rerun the command with the same key; pick a new key only for a new generation.
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" \
  -H "Idempotency-Key: fox-001" \
  -d '{"model":"nano-banana-2","prompt":"a red fox in the snow, film photo","params":{"aspect_ratio":"16:9","resolution":"2K"}}'

# Wait for the result: put generations[0].id from the answer above in place of GENERATION_ID,
# and repeat while "status" is "queued" or "processing"
curl -s "$API/generations/GENERATION_ID?wait=60" -H "$AUTH"

Примеры написаны для bash: macOS, Linux, WSL или Git Bash в Windows. В Windows PowerShell curl — псевдоним другой команды, поэтому используйте curl.exe и синтаксис PowerShell для переменных и кавычек либо запускайте примеры в Git Bash или WSL.

ВызовЧто возвращает
GET /meАккаунт и его balance.
GET /models?kind=imageМодели изображений, у каждой свои params, inputs и pricing.
POST /generations с "dryRun": trueЦену: cost за одну генерацию, total за весь запрос. Ничего не запускается.
POST /generations202 и generations[].id. Кредиты списываются в этот момент.
GET /generations/{id}?wait=60Генерацию. Повторяйте, пока status равен queued или processing; когда он станет done, файлы лежат в outputs[].url.

Заголовок Idempotency-Key обозначает именно эту генерацию. Если ответ потерялся, отправьте тот же запрос с тем же значением заголовка: вернётся первый ответ, а повторного списания не будет. См. Идемпотентность.

С входным файлом

Модели, которая работает с изображением, видео или аудиофайлом, нужно, чтобы файл сначала оказался на Salven. Этот пример загружает фото и превращает его в ролик:

bash
API=https://salven.ai/api/v1
AUTH="Authorization: Bearer $SALVEN_API_KEY"

# Upload the picture. The answer's "url" is the file's address on Salven:
# {"url":"https://cdn.salven.ai/uploads/...","kind":"image","mimeType":"image/jpeg","bytes":184233}
curl -s "$API/uploads" -H "$AUTH" -F "file=@photo.jpg"

# Put that url here
PHOTO_URL="PASTE_THE_URL_FROM_THE_ANSWER_ABOVE"

# Price first: the same body with dryRun
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" -d @- <<EOF
{
  "model": "kling-v3-pro",
  "prompt": "she turns and smiles, slow dolly in",
  "params": { "duration": 5 },
  "inputs": { "startImage": "$PHOTO_URL" },
  "dryRun": true
}
EOF

# Then run it, with a key that names this generation (reuse it only to retry this very request)
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" \
  -H "Idempotency-Key: anim-001" -d @- <<EOF
{
  "model": "kling-v3-pro",
  "prompt": "she turns and smiles, slow dolly in",
  "params": { "duration": 5 },
  "inputs": { "startImage": "$PHOTO_URL" }
}
EOF

# Wait for the clip: put generations[0].id from the answer above in place of GENERATION_ID,
# and repeat while "status" is "queued" or "processing" (video takes minutes)
curl -s "$API/generations/GENERATION_ID?wait=60" -H "$AUTH"

Входом может быть только ваш собственный файл: загруженный через POST /uploads или outputs[].url вашей же генерации. Ссылка на файл из любого другого места отклоняется с input_not_allowed: скачайте файл и загрузите его.

Аутентификация

В каждом запросе передаётся API-ключ: Authorization: Bearer <key>. Заголовок X-API-Key: <key> тоже принимается.

  • Ключ — это slv_ и за ним 40 букв и цифр.
  • Он показывается один раз, при создании. Salven хранит только его хеш, поэтому потерянный ключ нельзя показать снова: отзовите его и создайте новый.
  • У аккаунта может быть до 10 действующих ключей. Создавать и отзывать их можно в Настройках, в блоке Claude и API. Отозванный ключ перестаёт работать сразу.
  • Ключ действует от имени своего аккаунта: списываются кредиты этого аккаунта, результаты попадают в его галерею. Обращайтесь с ним как с паролем: храните в переменной окружения или в хранилище секретов и никогда не пишите в код, репозиторий или лог.
  • API проверяет только ключ. Браузер, в котором выполнен вход на salven.ai, не может вызвать API со своей cookie.
HTTPКодЧто значит
401unauthorizedКлюча нет, это не ключ Salven, он неизвестен или отозван.
403api_not_availableAPI недоступен для этого аккаунта.

Общие правила

  • Базовый URL: https://salven.ai/api/v1. Все пути на этой странице указаны относительно него.
  • Тела запросов и ответов — JSON. Только POST /uploads принимает multipart/form-data.
  • Время — в формате ISO 8601, в UTC.
  • Каждый ответ приходит с Cache-Control: no-store.
  • Деньги считаются в кредитах.
  • Ошибка от API — это { "error": { "code": …, "message": … } } с соответствующим HTTP-статусом. Веб-сервер перед API может ответить и без JSON-тела: 413 на тело запроса больше 25 МБ и 502 или 504, пока сайт перезапускается. См. Ошибки.

Как проходит генерация

  1. Баланс. GET /me возвращает balance: дороже этого запуск стоить не может.
  2. Модель. GET /models или один вид через ?kind=. Прочитайте спецификацию выбранной модели: params, inputs, aspect, modes, pricing. Отправляйте только те имена и значения, которые в ней перечислены.
  3. Загрузки. Только если модель принимает файлы: POST /uploads возвращает url, который используется как вход.
  4. Цена. POST /generations с "dryRun": true возвращает cost (одна генерация) и total (cost × count).
  5. Запуск. То же тело без dryRun, с заголовком Idempotency-Key. Ответ — 202 с generations[].id, кредиты списаны.
  6. Ожидание. GET /generations/{id}?wait=60; повторяйте, пока status равен queued или processing.
  7. Результат. outputs[].url — прямые ссылки на файлы.

Статусы

СтатусЧто значит
queuedПринята и оплачена, ждёт своей очереди.
processingСоздаётся.
doneГотова. Файлы лежат в outputs.
failedСоздать не удалось. Кредиты возвращены (refunded).

У неудавшейся генерации error.code равен generation_failed; причину, которую назвала сама модель, API не сообщает. Кредиты уже вернулись, так что можно попробовать ещё раз, возможно с другими настройками. После двух неудач подряд остановитесь и проверьте запрос.

Сколько это занимает

ЧтоОбычное время
Изображение10–60 секунд
Видео1–5 минут, дольше для роликов на 10–15 секунд
РечьОт нескольких секунд до полуминуты
МузыкаДо нескольких минут
3D-модель2–8 минут; некоторым моделям может понадобиться больше получаса
Риг для 3D-моделиОколо 2 минут

На стороне Salven зависший запуск ничто не завершает. Разумный клиент перестаёт ждать примерно через 5 минут для изображения, 10 для аудио, 20 для видео и 60 для 3D и сам не запускает ту же генерацию заново: запуск остаётся в галерее аккаунта.

Эндпоинты

ЭндпоинтЧто делает
GET /meАккаунт, которому принадлежит ключ, и его баланс.
GET /modelsВсе модели, которые может запустить API, при желании только одного вида.
GET /models/{id}Спецификация одной модели.
POST /uploadsСохраняет файл как ваш собственный и возвращает его URL.
POST /generationsЗапускает одну или несколько генераций либо называет их цену.
GET /generations/{id}Одна генерация: статус и, когда она готова, файлы.
GET /generationsГенерации аккаунта, сначала новые.
GET /charactersПерсонажи и инфлюенсеры аккаунта.
GET /characters/{handle}Один из них, с фото.
POST /charactersСоздаёт персонажа.
GET /effectsГотовые эффекты.
GET /effects/{slug}Спецификация одного эффекта.
POST /effects/{slug}/runЗапускает эффект либо называет его цену.

Числа в примерах на этой странице — только примеры. Актуальные цены — в GET /models и в таблице раздела Модели; точная цена запроса — та, что вернёт пробный запрос с тем же телом.

GET /me

json
{
  "id": "cmg1x2y3z0000abcd1234efgh",
  "username": "anna",
  "name": "Anna",
  "credits": 1200,
  "dailyAllowance": 0,
  "balance": 1200,
  "key": { "id": "cmg4k5m6n0001abcd5678ijkl", "name": "Claude Code", "prefix": "slv_Ab12Cd" }
}

credits не сгорают. dailyAllowance — остаток бесплатных ежедневных кредитов партнёра на сегодня; они тратятся первыми. balance — сумма обоих: дороже этого запуск стоить не может. key называет ключ, с которым пришёл запрос.

GET /models и GET /models/{id}

GET /models возвращает { "models": [ … ] } в том же порядке, что в студии. ?kind= сужает список до image, video, audio или 3d; на любое другое значение придёт отказ 400 invalid_kind. GET /models/{id} возвращает одну модель или 404 model_not_found. Модель выглядит так:

json
{
  "id": "kling-v3-pro",
  "name": "Kling v3 Pro",
  "kind": "video",
  "description": "Top-tier video quality, start from a frame",
  "pricing": { "credits": 20, "unit": "second", "secondsFrom": "params.duration" },
  "modes": [
    { "slug": "generate", "name": "Generate", "promptRequired": true, "priceMultiplier": 1 }
  ],
  "prompt": { "maxLength": 10000 },
  "negativePrompt": { "maxLength": 2500 },
  "aspect": null,
  "params": [
    {
      "name": "duration",
      "type": "number",
      "min": 3,
      "max": 15,
      "step": 1,
      "default": 5,
      "priced": true
    },
    {
      "name": "aspect_ratio",
      "type": "enum",
      "options": ["16:9", "9:16", "1:1"],
      "default": "16:9"
    },
    { "name": "audio", "type": "boolean", "default": false, "priceFactorWhenTrue": 1.5 }
  ],
  "inputs": [
    { "name": "startImage", "kind": "image", "required": false, "multiple": false, "max": 1 },
    {
      "name": "endImage",
      "kind": "image",
      "required": false,
      "multiple": false,
      "max": 1,
      "requires": "startImage"
    }
  ],
  "maxCount": 1,
  "startImageSetsShape": true,
  "seed": false,
  "references": null,
  "characters": { "supported": true, "maxCharacters": 3, "needsStartFrame": true }
}
ПолеКак его читать
pricingcredits за unit. generation — фиксированная цена. second умножается на секунды, а secondsFrom говорит, на какие: параметр duration или длина входного файла. 1000_characters — для речи: длина промпта. Результат умножается на priceMultiplier режима и на коэффициенты выбранных вариантов.
modesРежимы студии, в которых работает модель. Передавайте mode, только если их больше одного; по умолчанию берётся generate, а если его нет — первый. promptRequired: false значит, что модели достаточно входов и промпт не нужен.
prompt, negativePromptНаибольшая длина текста в символах. negativePrompt: null значит, что модель его не принимает.
paramsНастройки, передаются по name. number: в пределах min..max с шагом step. enum: ровно один из options; priceFactors даёт ценовой коэффициент каждого варианта, optionNotes поясняет, что это за вариант (например, пол и тон голоса). boolean: true или false, с priceFactorWhenTrue, если включённый переключатель стоит дороже. Пропущенный параметр берёт свой default; неизвестное имя отклоняется.
aspectНе null: соотношение сторон результата задаётся полем верхнего уровня aspect, одним из options. Null: у модели есть свой параметр aspect_ratio либо соотношение сторон определяется её входом.
inputsФайлы, передаются как inputs: { "<name>": "<url>" } либо массивом URL для входа с multiple, не больше max. kind — чем должен быть файл. Вход с required обязателен; вход с requires используется только вместе с тем входом, который там назван.
maxCountСколько отдельных генераций один запрос может запустить через count.
startImageSetsShapetrue: первый кадр задаёт соотношение сторон ролика, и aspect_ratio игнорируется, как только кадр передан.
seed, seedMaxПринимает ли модель seed и его наибольшее значение. См. Seed.
referencesОграничения для референсных медиафайлов у моделей, которые их принимают, иначе null.
charactersДобавляет ли @handle в промпте фото этого человека к запросу (supported), для скольких людей в одном запуске (maxCharacters) и обязателен ли при этом первый кадр (needsStartFrame). См. Персонажи.

Две вещи оставлены для SKILL.md: тип параметра motions, который используется при риггинге 3D-моделей, и правила блока references.

POST /uploads

Multipart-форма с полем file и, по желанию, kind (image, video, audio или model). Без kind вид определяется по первым байтам файла. Ответ — 201:

json
{
  "url": "https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847000000-9f2c4e6a8b0d1f35.jpg",
  "kind": "image",
  "mimeType": "image/jpeg",
  "bytes": 184233
}
ВидФорматыНаибольший файл
imageJPEG, PNG, WebP10 МБ
videoMP4, MOV, WebM25 МБ
audioMP3, WAV, M4A, OGG20 МБ
modelGLB (бинарный glTF 2.0)25 МБ
  • Формат читается из байтов, а не из имени файла.
  • Фото HEIC отклоняются с heic_not_supported: сначала конвертируйте их в JPEG.
  • 3D-модель должна быть одним файлом GLB с текстурами внутри; .gltf не принимается.
  • 25 МБ для видео и 3D-модели — это ограничение веб-сервера на всё тело запроса, поэтому сам файл должен быть немного меньше. Запрос с телом крупнее отклоняется веб-сервером ещё до того, как дойдёт до API: такой ответ — 413, и его тело не JSON.
  • Видео или модель GLB больше этого, до 60 МБ, можно передать только с публичного веб-адреса, через MCP-инструмент upload_from_url: см. Как передать Claude файл.
  • Файл становится вашей собственной загрузкой. Входами могут быть только ваши файлы: загрузки и outputs[].url ваших же генераций.

Отказы: 400 missing_file, invalid_form_data, invalid_kind; 413 file_too_large (изображение или аудиофайл больше своего лимита, с max в байтах); 415 unsupported_format, heic_not_supported; 502 storage_error (повторите один раз); 503 uploads_disabled.

POST /generations

Тело запроса
{
  "model": "nano-banana-2",
  "mode": "generate",
  "prompt": "a red fox in the snow, film photo",
  "params": { "aspect_ratio": "16:9", "resolution": "2K" },
  "count": 1,
  "enhancePrompt": true,
  "dryRun": false
}
ПолеЧто значит
modelОбязательно. ID модели из GET /models.
modeНеобязательно. Один из modes[].slug модели.
promptОбязательно, если у режима promptRequired равен true. Любой язык; @handle называет одного из ваших персонажей. См. Промпты.
negativePromptТолько там, где negativePrompt модели не null, длиной до его maxLength.
aspectТолько там, где aspect модели не null: один из его options.
paramsИмя параметра → значение, как они перечислены в params модели.
inputsИмя входа → URL файла либо массив URL для входа с multiple.
seedТолько там, где seed модели равен true: целое число от 0 до seedMax.
countОт 1 до maxCount отдельных генераций, каждая оплачивается. По умолчанию 1.
enhancePromptПо умолчанию true. false отправляет промпт в точности как написан.
dryRuntrue: вернуть цену, ничего не запускать и ничего не списывать.

Поле, которого нет в этом списке, отклоняется с invalid_request. С каждым настоящим запуском отправляйте заголовок Idempotency-Key; см. Идемпотентность.

На пробный запрос приходит ответ 200. cost — одна генерация, total — сколько будет списано за весь запрос:

Ответ на пробный запрос
{
  "dryRun": true,
  "model": "nano-banana-2",
  "mode": "generate",
  "cost": 45,
  "count": 1,
  "total": 45,
  "balance": 1200
}

На настоящий запуск приходит ответ 202:

Ответ
{
  "generations": [
    {
      "id": "cmg7q2w8e0003abcd9012mnop",
      "status": "queued",
      "kind": "image",
      "model": "nano-banana-2",
      "mode": "generate",
      "effect": null,
      "prompt": "a red fox in the snow, film photo",
      "characters": [],
      "seed": null,
      "source": "api",
      "cost": 45,
      "refunded": 0,
      "createdAt": "2026-10-01T09:30:00.000Z",
      "startedAt": null,
      "finishedAt": null,
      "outputs": [],
      "error": null
    }
  ],
  "charged": 45,
  "balance": 1155
}

Если что-то (обычно баланс) останавливает запрос, когда запущена только часть из count генераций, уже начатые остаются в силе, а в ответе дополнительно есть "partial": true и "stopped" с кодом причины в code. Чтобы запустить остальное, отправьте новый запрос с меньшим count и новым Idempotency-Key.

balance равен null, если после списания его не удалось прочитать; он есть в GET /me. Если не удалось прочитать только что созданные генерации, в generations будут только id и status каждой, а charged тоже будет null: опрашивайте эти ID как обычно.

GET /generations/{id}

json
{
  "id": "cmg7q2w8e0003abcd9012mnop",
  "status": "done",
  "kind": "image",
  "model": "nano-banana-2",
  "mode": "generate",
  "effect": null,
  "prompt": "a red fox in the snow, film photo",
  "characters": [],
  "seed": null,
  "source": "api",
  "cost": 45,
  "refunded": 0,
  "createdAt": "2026-10-01T09:30:00.000Z",
  "startedAt": "2026-10-01T09:30:01.000Z",
  "finishedAt": "2026-10-01T09:30:24.000Z",
  "outputs": [
    {
      "url": "https://cdn.salven.ai/generations/cmg7q2w8e0003abcd9012mnop/0.png",
      "mimeType": "image/png",
      "width": 2048,
      "height": 1152,
      "durationSeconds": null,
      "thumbnailUrl": null
    }
  ],
  "error": null
}

С ?wait=<seconds>, от 0 до 60, ответ задерживается, пока генерация не станет done или failed либо пока не пройдут эти секунды; тогда приходит ответ с текущим состоянием. Если ID не относится к генерациям аккаунта, приходит 404 not_found.

ПолеЧто значит
statusqueued, processing, done или failed.
kindimage, video, audio или 3d.
model, mode, promptЧто запускалось. У запуска эффекта они равны null, а effect содержит его slug, name и quality.
charactersЧьи фото добавлены по @-именам из промпта: у каждого handle, name и kind.
seedSeed, с которым прошёл запуск, если он был передан.
sourceОткуда запущено: web, bot или api.
cost, refundedСписанные кредиты и кредиты, возвращённые при неудаче.
createdAt, startedAt, finishedAtВремя. Последние два равны null, пока событие не произошло.
outputsФайлы, когда статус done: url, mimeType, width, height, durationSeconds, thumbnailUrl. До этого пусто.
errornull либо, у неудавшегося запуска, объект с code (generation_failed) и message.

Результат 3D — файл .glb. У такого результата могут быть ещё поля faces, а у модели с ригом — fbxUrl и clips; они описаны в SKILL.md.

GET /generations

Генерации аккаунта, сначала новые: все, а не только запущенные через API. Параметры запроса: limit (по умолчанию 20, до 50), kind (image, video, audio или 3d) и cursor. Ответ — { "generations": [ … ], "nextCursor": … }: передайте nextCursor как cursor, чтобы получить следующую страницу; на последней он равен null. На неверные параметры придёт 400 invalid_kind или invalid_cursor.

Персонажи

Персонаж — это человек, сохранённый в аккаунте: имя, @handle и от одного до четырёх фото одного и того же человека, первое из которых главное. Напишите в промпте @anna, и модель, которая принимает персонажей, получит вместе с запросом фото anna, так что результат сохранит её внешность. Упоминание не увеличивает цену. У AI-инфлюенсеров, созданных на salven.ai, тоже есть @-имена, и работают они так же.

  • GET /characters возвращает { "characters": [ … ] }: персонажи, сначала новые, затем инфлюенсеры. У каждого есть handle, name, kind (character или influencer), photos (сколько фото отправляет упоминание) и createdAt.
  • GET /characters/{handle} возвращает { "character": { … } } (структура показана ниже, в POST /characters) или 404 character_not_found. Здесь photos — список, главное фото первым, у каждого url, width и height; у инфлюенсера width и height равны null.
  • POST /characters создаёт персонажа. Это бесплатно, ответ — 201 с тем же { "character": { … } }.
POST /characters
{
  "name": "Anna",
  "handle": "anna",
  "photos": [
    "https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847000000-9f2c4e6a8b0d1f35.jpg",
    "https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847060000-3b7d9e1c5a2f4860.jpg"
  ]
}
Ответ
{
  "character": {
    "handle": "anna",
    "name": "Anna",
    "kind": "character",
    "photos": [
      {
        "url": "https://cdn.salven.ai/characters/chr_5d1f0a9c3e7b2468ace01357/4a6c8e0b2d4f6a81.jpg",
        "width": 1536,
        "height": 2048
      },
      {
        "url": "https://cdn.salven.ai/characters/chr_5d1f0a9c3e7b2468ace01357/b3d5f7a9c1e30264.jpg",
        "width": 1536,
        "height": 2048
      }
    ],
    "createdAt": "2026-10-01T09:30:00.000Z"
  }
}
ПолеПравила
name1–60 символов в одну строку.
handle2–30 символов из a-z, 0-9 и _, начинается с буквы. Уникально среди персонажей и инфлюенсеров аккаунта. Имена вроде image1, video2, audio3 и element1 зарезервированы.
photos1–4 URL ваших собственных изображений (загрузки или изображения из результатов ваших генераций): PNG, JPEG или WebP, не меньше 300 пикселей по каждой стороне, стороны отличаются не больше чем в 2,5 раза, на всех один и тот же человек. Salven хранит собственные копии.

Idempotency-Key здесь нет: ключом служит само @-имя. Создание, повторённое после потерянного ответа, получит отказ 409 handle_taken, если первое прошло, и тогда GET /characters/{handle} покажет персонажа. У аккаунта может быть до 50 персонажей (409 limit_reached). Редактирование и удаление — на странице персонажей.

Как использовать: персонажей принимает только модель, в спецификации которой characters.supported: true, не больше characters.maxCharacters за один запуск, а при characters.needsStartFrame: true нужен ещё и первый кадр. На любой другой модели упоминание вашего собственного @-имени отклоняется с character_not_supported, и ничего не списывается. @-имя, которого в аккаунте нет, — не ошибка: оно остаётся обычным текстом, и фото по нему не добавляются, поэтому берите @-имена из GET /characters и проверяйте characters готовой генерации.

Эффекты

Эффекты — это готовые стили со страницы эффектов: Salven уже задал модель, промпт и настройки, а вы даёте изображение или изображения и, для некоторых эффектов, строку текста. Запуск эффекта — обычная генерация.

GET /effects возвращает { "effects": [ … ] } в том же порядке, что на странице эффектов. GET /effects/{slug} возвращает один эффект или 404 effect_not_found:

json
{
  "slug": "wide-angle",
  "name": "Wide Angle",
  "description": "…",
  "kind": "image",
  "category": { "slug": "objects", "name": "Objects" },
  "preview": {
    "videoUrl": null,
    "posterUrl": "https://cdn.salven.ai/effects/cmg2a3b4c0002abcd3456qrst/preview.jpg",
    "width": 1024,
    "height": 1365
  },
  "inputs": [
    {
      "key": "object",
      "label": "Object",
      "hint": "…",
      "required": true,
      "exampleUrl": "https://cdn.salven.ai/effects/cmg2a3b4c0002abcd3456qrst/object.jpg"
    }
  ],
  "text": null,
  "pricing": { "unit": "image", "credits": 30 },
  "quality": {
    "default": "1K",
    "options": [
      { "value": "1K", "label": "1K", "pricing": { "unit": "image", "credits": 30 } },
      { "value": "2K", "label": "2K", "pricing": { "unit": "image", "credits": 45 } },
      { "value": "4K", "label": "4K", "pricing": { "unit": "image", "credits": 60 } }
    ]
  },
  "maxCount": 4
}
ПолеЧто значит
inputsИзображения, которые принимает эффект; каждое передаётся по своему key. required говорит, какие обязательны; exampleUrl — образец.
textnull либо текстовое поле эффекта: label, placeholder, maxLength, required.
pricingЭффект для изображений: credits за одно изображение. Видеоэффект: unit равен run, а durations перечисляет каждую доступную длительность с её seconds и credits.
qualitynull либо выбор, который предлагает эффект: default и options, у каждого варианта свои value, label и pricing. pricing верхнего уровня — цена варианта по умолчанию.
maxCountСколько изображений может создать один запрос.
POST /effects/{slug}/run
{
  "inputs": {
    "object": "https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847000000-9f2c4e6a8b0d1f35.jpg"
  },
  "quality": "4K",
  "count": 2,
  "language": "en",
  "expectedCost": 60,
  "dryRun": false
}
ПолеЧто значит
inputsКлюч входа → URL вашего собственного изображения (PNG, JPEG или WebP).
textТолько если у эффекта есть text; обязательно, если text.required равен true.
durationВидеоэффекты: одно из pricing.durations[].seconds. По умолчанию первое.
qualityТолько если у эффекта есть quality: одно из quality.options[].value, в точности как написано. По умолчанию quality.default.
countЭффекты для изображений: от 1 до maxCount изображений, каждое оплачивается. Видеоэффект всегда делает один запуск.
languageen или ru: язык названия эффекта, под которым запуск записывается в галерее. По умолчанию en.
expectedCostНеобязательно. Цена одного запуска, какой её назвал пробный запрос; если цена теперь другая, ответ — 409 price_changed.
dryRuntrue: вернуть цену, ничего не запускать.

В пробном запросе то же тело, включая изображения, а в ответе — cost (один запуск), count, total, quality и balance. На запуск приходит ответ 202, как у POST /generations, с теми же правилами Idempotency-Key; каждую генерацию опрашивайте через GET /generations/{id}. Обычный порядок: узнать цену через dryRun, затем отправить то же тело с expectedCost, равным полученному cost.

Модели

API предлагает ровно те модели, которые сейчас предлагает студия, в том же порядке. Эта таблица читается из актуального каталога. Цена указана базовая: до множителя режима и платных вариантов параметров.

Изображения

ID моделиНазваниеРежимыБазовая цена
nano-banana-2Nano Banana 2generate30 кр. / генерация
nano-banana-proNano Banana Progenerate56 кр. / генерация
seedream-v5-proSeedream v5 Progenerate40 кр. / генерация
gpt-image-2-5GPT Image 2.5generate20 кр. / генерация
gpt-image-2GPT Image 2generate60 кр. / генерация
flux-kontextFLUX.1 Kontextgenerate12 кр. / генерация
recraft-v4Recraft V4generate20 кр. / генерация
seedream-4Seedream 4generate25 кр. / генерация
qwen-image-3Qwen Image 3generate12 кр. / генерация
ideogram-v4Ideogram v4generate6 кр. / генерация
grok-imagine-2Grok Imagine 2generate18 кр. / генерация
virtual-try-onVirtual Try-Ongenerate, try-on25 кр. / генерация
image-upscalerImage Upscalergenerate, upscale12 кр. / генерация

Видео

ID моделиНазваниеРежимыБазовая цена
kling-v3-proKling v3 Progenerate20 кр. / секунда
veo-3Google Veo 3generate600 кр. / генерация
kling-v3-pro-motion-controlKling v3 Pro · Motion Controlmotion-control20 кр. / секунда
video-upscalerVideo Upscaler · RealESRGANupscale20 кр. / секунда
veo-3-1-fastGoogle Veo 3.1 Fastgenerate220 кр. / генерация
hailuo-02MiniMax Hailuo 02generate120 кр. / генерация
seedance-v1-proSeedance v1 Progenerate150 кр. / генерация
minimax-h3-max-turboMiniMax H3-Max Turbogenerate4 кр. / секунда
minimax-h3-maxMiniMax H3-Maxgenerate12 кр. / секунда
wan-3Wan 3.0generate18 кр. / секунда
lipsync-photoLipsync — from a photolipsync24 кр. / секунда
lipsync-redubLipsync — redub a cliplipsync13 кр. / секунда
video-to-audioSound for videosound10 кр. / генерация
seedance-2-5Seedance 2.5generate85 кр. / секунда

Аудио

ID моделиНазваниеРежимыБазовая цена
minimax-speech-hdMiniMax Speech 2.8 HDtts18 кр. / 1000 символов
minimax-speech-turboMiniMax Speech 2.8 Turbotts11 кр. / 1000 символов
gemini-3-8-flash-ttsGemini 3.8 Flash TTStts9 кр. / 1000 символов
gemini-3-8-flash-lite-ttsGemini 3.8 Flash Lite TTStts6 кр. / 1000 символов
elevenlabs-tts-v3ElevenLabs v3tts18 кр. / 1000 символов
elevenlabs-tts-multilingual-v2ElevenLabs Multilingual v2tts18 кр. / 1000 символов
elevenlabs-tts-turbo-v2-5ElevenLabs Turbo v2.5tts9 кр. / 1000 символов
xai-ttsxAI TTStts3 кр. / 1000 символов
inworld-tts-1-5-maxInworld TTS 1.5 Maxtts2 кр. / 1000 символов
lyria-3-proLyria 3 Promusic15 кр. / генерация
stable-audio-3Stable Audio 3music7 кр. / генерация
minimax-musicMiniMax Music 2.6music27 кр. / генерация
lyria-3Lyria 3music8 кр. / генерация
elevenlabs-music-v2-5ElevenLabs Music v2.5music108 кр. / генерация
elevenlabs-sfxElevenLabs Sound Effectssfx4 кр. / генерация
sonilo-sfxSonilo Sound Effectssfx4 кр. / генерация

3D

ID моделиНазваниеРежимыБазовая цена
tripo-h3-1Tripo H3.1image54 кр. / генерация
tripo-p2Tripo P2image198 кр. / генерация
hunyuan-3d-proHunyuan 3D Proimage68 кр. / генерация
rodin-2-5Rodin 2.5image72 кр. / генерация
trellis-2Trellis 2image54 кр. / генерация
tripo-h3-1-textTripo H3.1text36 кр. / генерация
rodin-2-5-textRodin 2.5text72 кр. / генерация
meshy-7-viewsMeshy V7views216 кр. / генерация
meshy-riggingMeshy Rigginganimate36 кр. / генерация
meshy-rigging-multiMeshy Rigging Multi-Animationanimate36 кр. / генерация

Список меняется: модели добавляются и скрываются. Программе стоит читать GET /models, а не держать собственную копию, а цену запроса узнавать пробным запросом с тем же телом.

Промпты и seed

Пишите промпты на любом языке. По умолчанию (enhancePrompt: true) Salven переводит промпт на английский и переписывает его под модель. Промпт для изображения или видео превращается самое большее примерно в 80 английских слов; длинный подробный английский промпт остаётся почти без изменений.

enhancePrompt: false отправляет промпт в точности как написан, и негативный промпт тоже. Используйте это для тщательно составленного английского промпта, для длинного текста песни или для точной команды редактирования вроде «make the jacket red, change nothing else».

  • Текст в изображении. Слова, которые должны быть написаны на изображении, берите в кавычки, например a cake with the inscription «Happy birthday».
  • Речь. Модель речи (режим tts) читает промпт дословно, на его языке; он никогда не переводится. Произносится и оплачивается весь промпт, поэтому отправляйте только те слова, которые нужно сказать.
  • Музыка и звуки. Промпт превращается в короткое описание на английском. Слова, которые нужно спеть или произнести, остаются как написаны и берутся в кавычки.
  • 3D-модели и чистые редакторы. Промпт только переводится, без приукрашивания. Модель изображений, которая принимает референсные изображения, — не чистый редактор: её промпт переписывается, как любой другой, поэтому точную команду редактирования отправляйте на английском с enhancePrompt: false.
  • Негативные промпты. Только у моделей, в спецификации которых есть negativePrompt. По умолчанию негативный промпт переводится на английский и никогда не приукрашивается.
  • Длина. Держитесь в пределах prompt.maxLength модели, иначе запрос отклоняется с prompt_too_long (с max и length).
  • Персонажи. @handle одного из ваших персонажей переживает перевод и переписывание. Любое другое @word — обычный текст. См. Персонажи.

Перевод не гарантирован: когда он не может выполниться, промпт уходит как набран. Для моделей изображений, видео и 3D надёжный выбор — английский.

Seed

Seed — это число, которое задаёт случайность модели. Тот же seed с тем же промптом, входами и параметрами даёт тот же результат снова или почти тот же, так что понравившийся результат можно повторить, изменив что-то одно.

  • Seed принимает только модель, в спецификации которой "seed": true, от 0 до seedMax. На любой другой модели он отклоняется с seed_not_supported, а вне диапазона — с invalid_seed.
  • Seed никогда не меняет цену.
  • Если вместе с seed передан count, генерации получают seed, seed + 1, seed + 2 и так далее.
  • Для точного повтора оставьте enhancePrompt таким же, как в первом запуске; надёжнее всего false.
  • seed готовой генерации показывает, с чем она запускалась.

Идемпотентность

Старт генерации списывает кредиты, поэтому случайно произойти дважды он не должен. Когда ответ потерян (таймаут, обрыв соединения, 5xx), нельзя понять, начался ли запуск. Заголовок Idempotency-Key делает повтор безопасным: тот же запрос с тем же ключом возвращает первый ответ, а не запускает генерацию заново.

  • Отправляйте его с POST /generations и POST /effects/{slug}/run.
  • Ключ — 1–128 символов из A-Z, a-z, 0-9, ., _, : и -; всё остальное отклоняется с 400 invalid_idempotency_key. Подойдёт UUID.
  • Для каждой новой генерации берите новый ключ, а для каждого её повтора — тот же.
  • Воспроизведённый ответ приходит с заголовком Idempotent-Replayed: true.
  • Запоминается только ответ, после которого генерации начались (202). Отказ вроде 400, 402 insufficient_credits или 429 too_many_running не запоминается: когда его причина устранена, тот же запрос с тем же ключом выполнится.
  • 202 окончателен для своего ключа, включая частичный: повтор воспроизводит этот ответ и ничего не запускает.
  • Ключи хранятся 24 часа.
  • Пробный запрос никогда не запоминается и не воспроизводится: он только называет цену.
HTTPКодЧто значит
422idempotency_key_reusedКлюч уже использован с другим телом. Для другого запроса возьмите новый ключ.
409idempotency_in_progressПервый запрос с этим ключом ещё обрабатывается. Подождите несколько секунд и повторите его с тем же ключом.

Если 409 приходит дольше нескольких минут, возможно, первая попытка оборвалась уже после старта. Посмотрите в GET /generations, не появились ли с тех пор запуски, и отправляйте запрос с новым ключом, только если ничего не началось.

Ошибки

У всех ошибок, которыми отвечает сам API, одна структура. Стройте обработку на code: он стабилен. message — на английском и написано для людей. Остальные поля — подробности этой ошибки. У ответа, который сформировал не сам API, такого тела нет: 413 веб-сервера на тело запроса больше 25 МБ, 502 или 504 без JSON-тела, пока сервер перезапускается, или 5xx без кода. Проверяйте Content-Type, прежде чем разбирать тело ошибки, а 5xx без кода обрабатывайте так, как сказано в последней строке таблицы.

json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough credits for this generation",
    "required": 45,
    "available": 12
  }
}

За отклонённый запрос ничего не списывается. За генерацию, которая не удалась позже (status — failed), кредиты возвращаются.

HTTPКодЧто делать
400invalid_json, invalid_request, invalid_prompt, invalid_params, invalid_inputsИсправьте тело; сообщение называет поле.
400invalid_idempotency_keyИсправьте заголовок. См. Идемпотентность.
400invalid_kind, invalid_cursorИсправьте параметры запроса GET /models или GET /generations.
400unknown_param, invalid_param_value, unknown_input, input_kind, input_missing, input_requires, input_takes_one_file, invalid_input_url, too_many_files, aspect_not_supported, invalid_aspect, prompt_required, negative_prompt_not_supported, count_too_largeПеречитайте спецификацию модели. Подробности говорят, что разрешено: allowed, expected, max, input, param.
400input_not_allowedURL — не ваш собственный файл. Сначала загрузите его через POST /uploads.
400prompt_too_long, negative_prompt_too_longСократите текст до max символов.
400seed_not_supported, invalid_seedНе передавайте seed для этой модели либо держите его в пределах 0..seedMax.
400media_missing, media_unreadable, media_foreign, reference_*Входной файл не читается или нарушает ограничения модели.
400model_too_dense, model_untextured, model_unreadable, model_foreign, model_missingДля 3D-модели, которую нужно анимировать, нельзя сделать риг: слишком много граней, нет текстуры, GLB не читается или файл не ваш.
400character_not_supported, character_too_many, character_needs_start_frame, character_with_frame, character_with_extend, character_no_room, character_no_photosПромпт называет ваших персонажей, а запрос не может с ними выполниться. Выберите модель, спецификация которой принимает персонажей, назовите меньше @-имён или следуйте сообщению. См. Персонажи.
400name_invalid, handle_invalid, handle_reserved, photos_required, too_many_photos, photo_foreign, photo_unreadable, photo_too_small, photo_aspectPOST /characters: исправьте поле. Отказ из-за фото называет это фото (index, photo).
400text_required, text_too_long, duration_value, quality_valueЭффекты: перечитайте спецификацию эффекта.
400, 413, 415missing_file, invalid_form_data, file_too_large, unsupported_format, heic_not_supportedЗагрузки: см. правила загрузки.
401unauthorizedКлюча нет, он неверный или отозван.
402insufficient_creditsСравните required и available. Пополните баланс на salven.ai; не повторяйте запрос.
403api_not_availableAPI недоступен для этого аккаунта.
404model_not_found, mode_not_available, not_found, effect_not_found, character_not_foundСверьте ID с соответствующим списком: GET /models, GET /generations, GET /effects, GET /characters. Подробности mode_not_available перечисляют modes модели.
409handle_taken, limit_reachedPOST /characters: @-имя занято (или это был повтор, а первое создание прошло) либо у аккаунта уже столько персонажей, сколько разрешено.
409price_changedЭффекты: цена изменилась с тех пор, как её назвал пробный запрос. cost — цена одного запуска сейчас. Запросите цену заново.
409idempotency_in_progressПодождите несколько секунд и повторите с тем же ключом. См. Идемпотентность.
422idempotency_key_reusedДля другого запроса возьмите новый Idempotency-Key.
429rate_limitedПодождите retryAfter секунд; то же говорит заголовок Retry-After.
429too_many_runningСейчас в очереди или в работе running ваших запусков через API, и этот запрос превысил бы предел. Дождитесь, пока часть завершится, и повторите его; тот же Idempotency-Key подойдёт.
5xxinternal_error, storage_error, uploads_disabled или вообще без кодаПовторите один раз после короткой паузы, с тем же Idempotency-Key. Перед повтором дорогого запроса посмотрите в GET /generations, нет ли запуска за последнюю минуту. 502 или 504, тело которых не JSON, приходят от веб-сервера во время перезапуска и могли оборвать запрос на полпути: тогда повтор с тем же ключом вернёт первый результат или 409.

Полный список со всеми полями подробностей — в SKILL.md.

Лимиты

ЧтоЛимит
Запросы120 в минуту на ключ, включая чтение
Запросы на генерацию: POST /generations и POST /effects/{slug}/run, включая пробные запросы30 в минуту на аккаунт
Загрузки30 в минуту на аккаунт, POST /uploads и MCP-инструмент upload_from_url вместе
Создание персонажей20 в минуту на аккаунт
Запуски через API, которые одновременно стоят в очереди или создаются10 на аккаунт, генерации и эффекты вместе
Тело JSON64 КБ
waitДо 60 секунд
limit у GET /generationsПо умолчанию 20, до 50
countДо maxCount модели
Промптprompt.maxLength модели, но не больше 10 000 символов; негативный промпт — не больше 4000
Размер файла, который вы отправляете сами: POST /uploads, ссылка для загрузкиИзображения 10 МБ, аудио 20 МБ, видео и модели GLB 25 МБ
Размер файла, скопированного с веб-адреса: MCP-инструмент upload_from_urlИзображения 10 МБ, аудио 20 МБ, видео и модели GLB 60 МБ
Ключи идемпотентностиХранятся 24 часа
API-ключи10 действующих ключей на аккаунт
Персонажи50 на аккаунт, у каждого 1–4 фото

В REST при превышении лимита частоты приходит 429 rate_limited с retryAfter в секундах и заголовком Retry-After. Запрос, чей count вывел бы число открытых запусков за 10, получает 429 too_many_running с running — числом открытых сейчас.

Для агентов: SKILL.md

SKILL.md — файл с инструкцией для агентов, которые пишут код, таких как Claude Code и Cursor. Это полный справочник по API, написанный для чтения агентом: все эндпоинты и коды отказов, правила для референсных медиафайлов, речи и риггинга 3D-моделей и то, как вести себя с чужими кредитами (сначала назвать цену, не зацикливаться на неудачах, никогда не выводить ключ).

  1. Создайте ключ в Настройках и задайте его как SALVEN_API_KEY в окружении, где работает агент.
  2. Дайте агенту файл. В Claude Code сохраните его как .claude/skills/salven-api/SKILL.md в проекте или как ~/.claude/skills/salven-api/SKILL.md для всех проектов. Другому агенту добавьте файл в его инструкции или укажите адрес выше.
  3. Попросите то, что вам нужно. Агент прочитает каталог, назовёт цену и выполнит запрос.

Агенту, который умеет работать с MCP, не нужны ни файл, ни ключ: вместо этого добавьте сервер из раздела Подключить Claude.

Помощник на Python

SKILL.md заканчивается небольшим клиентом на Python: он узнаёт цену, спрашивает согласия, запускает генерацию с Idempotency-Key, ждёт и повторяет запрос при лимитах частоты, занятых слотах и перезапусках. Ему нужен пакет requests.

python
import os, time, uuid, requests

API = "https://salven.ai/api/v1"
H = {"Authorization": f"Bearer {os.environ['SALVEN_API_KEY']}"}

def upload(path):
    with open(path, "rb") as f:
        r = requests.post(f"{API}/uploads", headers=H, files={"file": f})
    r.raise_for_status()
    return r.json()["url"]

class StillInProgress(RuntimeError):
    """The request may have started: check GET /generations before sending it under a new key."""

def call(method, url, **kw):
    """One request, retried through rate limits, busy slots, restarts and one server error.
    Every retry sends the same arguments, so a POST keeps its Idempotency-Key."""
    busy_since, server_errors = None, 0
    for attempt in range(80):
        try:
            r = requests.request(method, url, timeout=(10, 150), **kw)
        except (requests.ConnectionError, requests.Timeout, requests.exceptions.ChunkedEncodingError):
            time.sleep(10)                     # a restart, a dropped link: same request again
            continue
        if r.status_code == 409:               # idempotency_in_progress
            busy_since = busy_since or time.time()
            if time.time() - busy_since > 300:
                raise StillInProgress(url)
            time.sleep(10)
            continue
        if r.status_code == 429:
            code = r.json().get("error", {}).get("code") if "json" in r.headers.get("content-type", "") else None
            time.sleep(60 if code == "too_many_running" else int(r.headers.get("Retry-After", 10)))
            continue
        if r.status_code >= 500 and server_errors == 0:
            server_errors += 1
            time.sleep(10)
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError(f"{method} {url}: gave up")

MAX_WAIT_MIN = {"image": 5, "audio": 10, "video": 20, "3d": 60}   # past this a run counts as stuck

def generate(body, confirm):
    """Quote, ask, run, wait. Returns the output URLs and what did not finish."""
    quote = call("POST", f"{API}/generations", headers=H, json={**body, "dryRun": True})
    if quote["total"] > quote["balance"]:
        raise RuntimeError(f"needs {quote['total']} credits, the balance is {quote['balance']}")
    if not confirm(quote["total"]):            # total = cost × count
        return None
    kind = call("GET", f"{API}/models/{body['model']}", headers=H)["kind"]
    key = str(uuid.uuid4())                    # one key for this request and all its retries
    started = call("POST", f"{API}/generations", headers={**H, "Idempotency-Key": key}, json=body)
    result = {"urls": [], "failed": [], "stuck": [], "stopped": (started.get("stopped") or {}).get("code")}
    deadline = time.time() + MAX_WAIT_MIN[kind] * 60
    for gen in started["generations"]:
        while gen["status"] in ("queued", "processing") and time.time() < deadline:
            gen = call("GET", f"{API}/generations/{gen['id']}", headers=H, params={"wait": 60})
        if gen["status"] == "done":
            result["urls"] += [o["url"] for o in gen["outputs"]]
        elif gen["status"] == "failed":
            result["failed"].append(gen["id"])     # its credits were returned
        else:
            result["stuck"].append(gen["id"])      # tell the user; do not start it again
    return result                                  # "stopped": why fewer than `count` started, if so

start = upload("portrait.jpg")
print(generate({"model": "kling-v3-pro", "prompt": "she laughs and looks at the camera",
                "params": {"duration": 5}, "inputs": {"startImage": start}},
               confirm=lambda credits: input(f"Spend {credits} credits? [y/N] ").strip().lower() == "y"))

Полезно знать

Через API нельзя сделать следующее; это делается на salven.ai:

  • публикация работы в сообществе;
  • удаление работ;
  • редактирование и удаление персонажей (страница персонажей);
  • создание AI-инфлюенсеров (существующий используется по своему @handle, как персонаж);
  • пополнение баланса;
  • мультишот-ролики Kling.

URL результатов невозможно угадать, но они публичны. Файл откроет любой, у кого есть ссылка, поэтому делитесь ею только с теми, кому его стоит видеть.

Персонажи реальных людей. Создавайте персонажа только из своих фото или фото людей, которые на это согласились, и никогда не используйте его, чтобы создать впечатление, будто реальный человек сказал или сделал то, чего не говорил и не делал.

Вопросы и проблемы: Поддержка или письмо на support@salven.ai.