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

Вызовы, которые можно скопировать сверху вниз. Все они идут на https://api.coremind.ru и подписываются одним заголовком Authorization: Bearer cmk_… — как именно устроен ключ, описано в аутентификации. Все имена полей в запросах и ответах — snake_case.

0. Получить ключ

Войдите на сайт, откройте личный кабинет и в блоке «API-ключи» нажмите «Создать ключ». Ключ показывается один раз при создании — сразу сохраните его, например в переменную окружения COREMIND_API_KEY. Чтение, каталог и цены доступны и без тарифа, а для запуска генераций у аккаунта должен быть активный тариф.

1. Проверить ключ

GET /v1/whoami отвечает тем, во что разрешился ключ: user_id, key_id и scopes — список областей, которыми ключ располагает. Если этот вызов прошёл, ключ и заголовок собраны верно.

curl -s https://api.coremind.ru/v1/whoami \
  -H "Authorization: Bearer cmk_..."

2. Узнать аккаунт

GET /v1/me не принимает ни одного параметра: субъект берётся из ключа, чужой аккаунт прочитать нельзя. Ветвиться здесь обычно нужно по plan.id, по credits.main_cc — баланс в сантикредитах, как и все кредитные числа этого API, — и по free_quotas[].remaining.

Бесплатные генерации тарифа доступны через API. Строки free_quotas с mode: "unlimited" бесплатны только в очереди: передайте в запросе promo_mode: "queue", иначе генерация спишет кредиты. Доступность безлимитных генераций через API не гарантируется — что доступно прямо сейчас, показывает GET /v1/me → free_quotas.

curl -s https://api.coremind.ru/v1/me \
  -H "Authorization: Bearer cmk_..."

3. Получить каталог

GET /v1/models перечисляет модели, которые этот ключ может запустить. Модели типов, которые API не отправляет, выключенные модели и модели раннего доступа без доступа в список не попадают: чего в нём нет, то отправлять не нужно. Запись модели несёт id, type, constraints — что модель требует на входе — и pricing, где у каждого варианта цены указана единица, в которой она посчитана: цена за секунду и цена за ролик — разные числа. Полный перечень полей — в справочнике. Необязательный ?type= сужает выдачу.

curl -s "https://api.coremind.ru/v1/models?type=video" \
  -H "Authorization: Bearer cmk_..."

Справка и гайды по промптам лежат рядом, под той же областью models:read. GET /v1/knowledge?q=&limit= ищет по заголовку и краткому описанию и отвечает в конверте с next_cursor: null: выдача ограничена limit, а не разбита на страницы. Без q приходят только первые limit материалов в редакционном порядке, а не вся база: конкретную статью находят поиском по её словам. GET /v1/knowledge/{slug} отдаёт одну статью целиком. Здесь только публичные статьи и гайды, в том языке, на котором они написаны, — его называет поле lang. Неизвестный или непубличный slug одинаково отвечает not_found.

4. Прочитать свои генерации

Коллекция — GET /v1/generations — отвечает конвертом { data, next_cursor }: next_cursor нужно передать обратно без изменений в ?cursor=, а null означает, что страниц больше нет. Границы limit и правила курсора — на странице лимитов. Одну генерацию целиком читает GET /v1/generations/{id}, и форма записи там та же, что в списке.

curl -s "https://api.coremind.ru/v1/generations?limit=20" \
  -H "Authorization: Bearer cmk_..."

5. Следить за генерациями

За продвижением генераций следят по потоку событий GET /v1/events: каждая смена статуса приходит в него сама, и спрашивать ничего не нужно. Как подключиться и переподключаться без потерь — на странице событий.

POST /v1/generations/status — способ добрать пропущенное: после оборванного потока, который не удалось продолжить, или после кадра reset. Он принимает до 50 идентификаторов за раз и возвращает те же записи в data — спрашивайте только о тех, что не дошли до конечного статуса; одну генерацию перечитывает GET /v1/generations/{id}. Конечен DONE: результат готов и уже не изменится. QUEUED, PENDING, RUNNING и MODERATION_PENDING означают, что работа продолжается. FAILED завершает попытку, но не запись: если провайдер ответит с опозданием, та же генерация может перейти в DONE вместе с output_urls и refunded — поток сообщит и об этом. Весь словарь status опубликован в справочнике. Это чтение, а не запись: обоим маршрутам нужна область generations:read.

curl -s -X POST https://api.coremind.ru/v1/generations/status \
  -H "Authorization: Bearer cmk_..." \
  -H "Content-Type: application/json" \
  -d '{"ids": ["<generation_id_1>", "<generation_id_2>"]}'

Каждая ссылка в output_urls ведёт на сам API — GET /v1/generations/{id}/outputs/{kind}/{index} — и отдаёт файл только с тем же заголовком Authorization: без ключа владельца по ней ничего не скачать. Берите ссылку как есть, не собирайте её сами.

6. Библиотека

GET /v1/library/assets перечисляет загруженные референсы вызывающего, новые первыми, в том же конверте { data, next_cursor } и с теми же ?cursor= и ?limit=. У записи есть kind, размеры, folder_id и expires_at: загрузка живёт не вечно, и это поле говорит, до какого момента. GET /v1/library/folders параметров не принимает — папок у аккаунта ограниченное число, поэтому маршрут отвечает всем списком сразу, а next_cursor там всегда null. Обоим маршрутам нужна область library:read. Сам файл референса отдаёт GET /v1/library/assets/{id}/content — эта ссылка и лежит в output_urls записи.

curl -s "https://api.coremind.ru/v1/library/assets?limit=20" \
  -H "Authorization: Bearer cmk_..."

curl -s https://api.coremind.ru/v1/library/folders \
  -H "Authorization: Bearer cmk_..."

Разложить библиотеку можно тем же ключом с областью library:write: POST /v1/library/folders создаёт папку, PATCH /v1/library/folders/{id} меняет её name или position, DELETE /v1/library/folders/{id} удаляет только саму папку — её содержимое возвращается в корень. POST /v1/library/move переносит item_ids в folder_id, а null там означает корень. Заголовок меняют PATCH /v1/library/assets/{id} у референса и PATCH /v1/generations/{id} у генерации. Кредитов эти маршруты не тратят. Чужой идентификатор отвечает not_found, как и несуществующий. Переполненная библиотека — 409 folder_limit_reached, уже занятое имя папки — 409 folder_name_taken.

7. Загрузить референс

Свой файл отдаётся хранилищу напрямую, а не нам: POST /v1/uploads подписывает ровно один PUT и возвращает url, headers, reference и expires_at. Маршруту нужна отдельная область uploads:write; кредиты он не тратит. Ответ — 200, а не 201: объекта ещё нет, он появится, когда вы выполните PUT.

expires_at здесь — срок годности самой подписи, а не файла: после него этот url перестаёт приниматься хранилищем, и нужен новый билет. Сколько проживёт уже загруженный файл, говорит expires_at его записи в GET /v1/library/assets — это другое поле и другой отсчёт, поэтому истёкшую подпись нельзя читать как признак того, что reference больше не годится.

В запросе kind, content_type и content_length, и все три проверяются до того, как подпись вообще появится. Подписывается kind: "image"; остальные медиа отвечают 503 с кодом upload_unavailable. Разрешённые типы — image/jpeg, image/png, image/webp, video/mp4, video/quicktime, video/webm, audio/mpeg, audio/mp3, audio/wav, audio/wave, audio/x-wav, audio/ogg, audio/aac; потолок размера — 40 МиБ, и точное число в байтах повторяется в тексте отказа. content_length — не максимум, а точная длина: подпись закрепляет именно её.

SIZE=$(wc -c < reference.jpeg)

curl -s -X POST https://api.coremind.ru/v1/uploads \
  -H "Authorization: Bearer cmk_..." \
  -H "Content-Type: application/json" \
  -d '{"kind": "image", "content_type": "image/jpeg", "content_length": '"$SIZE"'}'

# в ответе url, proxy_path, headers, reference и expires_at — этот PUT идёт мимо нас, в хранилище
# длина подписана вместе с адресом: другое число — и хранилище откажет
curl -s -X PUT "<url>" \
  -H "Content-Type: image/jpeg" \
  -H "Content-Length: $SIZE" \
  -H "If-None-Match: *" \
  --data-binary @reference.jpeg

PUT нужно выполнить ровно с теми заголовками, что пришли в headers: подпись связывает их, и на расхождении хранилище отвечает 403. Билет записывает свой адрес один раз, поэтому повторный PUT по тому же url получает 412 — если загрузку нужно повторить, берите новый билет. Загруженный файл сразу учтён в библиотеке аккаунта.

Если прямой PUT до хранилища не доходит — так бывает в сетях, которые режут этот хост на теле чуть крупнее нескольких десятков килобайт, — тот же билет можно закрыть через нас: PUT /v1/uploads/content с тем же Content-Type и теми же байтами в сыром теле. Адрес уже собран в поле proxy_path ответа POST /v1/uploads, выбирать между путями по гео не нужно: оба выдаются сразу, берите тот, который проходит. Область та же — uploads:write.

SIZE=$(wc -c < reference.jpeg)

# тот же билет, но через наш хост — в ответе это поле proxy_path
curl -s -X PUT "https://api.coremind.ru<proxy_path>" \
  -H "Authorization: Bearer cmk_..." \
  -H "Content-Type: image/jpeg" \
  -H "Content-Length: $SIZE" \
  --data-binary @reference.jpeg
# {"reference": "<reference>", "bytes_stored": <SIZE>}

Билет пишет один раз, каким бы путём вы им ни воспользовались, поэтому повторное завершение здесь отвечает 409 duplicate_key_conflict — это тот же запрет, что и 412 от хранилища на прямом url, просто на него отвечает другая сторона. Тип и точную длину задаёт билет: расхождение отказывается, а не сохраняется.

Дальше в дело идёт reference — это то, что отправляется обратно при генерации, например в image_urls. Сами байты проверяются при отправке генерации, а не при загрузке: подпись здесь не означает, что файл принят.

8. Узнать цену заранее

POST /v1/pricing/estimate принимает то же самое тело, что и отправка генерации, и отвечает, во что она обойдётся: price_cc в сантикредитах, model_id, а в breakdown — variant (какая строка цены сработала), units (на что умножена ставка) и surcharge_cc (что добавлено сверху). Цену считает та же функция, которая потом спишет.

price_cc — это потолок, а не точное списание: бесплатная квота и промо-режим отрабатывают после расчёта и могут только уменьшить сумму. Маршрут ничего не создаёт и не тратит, ему хватает области models:read. Повтор ничего не создаёт и не списывает, но расходует общий лимит запросов, как любой вызов /v1.

Там, где цена зависит от файла — длительности трека или клипа, размера картинки для апскейла, — оценка измеряет сам файл на сервере, как это сделает списание, поэтому файл должен уже существовать и читаться; иначе ответ — 400, а не число. Запрос с promo_mode оценить нельзя: ответ будет 422 с кодом estimate_unavailable, а не придуманное число. Какие значения модель вообще принимает, публикует GET /v1/models: тело, которое отправка отвергнет, эта оценка отвергает тоже. Модель, которой нет в каталоге, оценка не считает, а тип, который API не принимает, получает тот же 503 с кодом submit_unavailable, что и отправка.

Числа в ответе ниже взяты для образца:

curl -s -X POST https://api.coremind.ru/v1/pricing/estimate \
  -H "Authorization: Bearer cmk_..." \
  -H "Content-Type: application/json" \
  -d '{"type": "image", "model": "<model_id>", "prompt": "..."}'
# {"type": "image", "model_id": "<model_id>", "currency": "credits",
#  "price_cc": 1200, "breakdown": {"variant": "default", "units": 1, "surcharge_cc": 0}}

9. Прочитать биллинг

GET /v1/billing/transactions перечисляет движения кредитов аккаунта, новые первыми, в конверте { data, next_cursor } с теми же ?cursor= и ?limit=. amount_cc — знаковое число в сантикредитах: положительное пополняет баланс, отрицательное списывает. У списания за генерацию заполнен generation_id — тот самый id, который принимает GET /v1/generations/{id}; у записей без такой связи, в том числе у старой истории, там null.

GET /v1/billing/subscription отвечает действующей подпиской в поле subscription или null, если её нет, — не 404. Суммы там в целых единицах currency, а не в сантикредитах. Тариф, баланс и бесплатные квоты здесь не повторяются — их отдаёт GET /v1/me. Платежи через API не видны и не создаются. Обоим маршрутам нужна область billing:read.

curl -s "https://api.coremind.ru/v1/billing/transactions?limit=20" \
  -H "Authorization: Bearer cmk_..."

curl -s https://api.coremind.ru/v1/billing/subscription \
  -H "Authorization: Bearer cmk_..."

Управление подпиской на сайте

В спецификации также описаны действия личного кабинета. Они доступны только с сессионной cookie сайта: API-ключ и OAuth-токен получают 403 insufficient_scope, в том числе с областью billing:read. Для этих действий откройте страницу подписки в личном кабинете.

  • POST /v1/subscription/resume — возобновить автопродление; ответ содержит next_pay_date.
  • POST /v1/subscription/cancel — отключить автопродление. Тело принимает необязательные reason и reason_text; ответ содержит plan_ends_at и superseded — признак того, что тариф уже продлевает новая подписка.
  • POST /v1/subscription/card-change — начать смену карты и получить payment_id с параметрами платёжного виджета.
  • POST /v1/subscription/card-change/complete — завершить смену карты после виджета. Тело содержит payment_id и необязательный transaction_id; ответ — card, next_pay_date, resumed и charge, результат оплаты просроченного периода.

10. Написать в поддержку

У аккаунта один тред поддержки, и это тот же чат, что открывается на сайте. GET /v1/support/messages отвечает им целиком в data, а next_cursor там всегда null; если обращений ещё не было — пустым списком, не 404. Чтение отмечает тред прочитанным, как открытие чата. У сообщения from — user или support, а text приходит ровно в том виде, в каком его написали.

POST /v1/support/messages отправляет в тот же тред text и до трёх картинок в images — это reference из POST /v1/uploads, не ссылки. client_msg_id — UUID, который вы придумываете сами: повтор с ним после потерянного ответа вернёт уже отправленное сообщение, а не создаст второе. Как устроен этот повтор, описано в идемпотентности. Чтению нужна область support:read, отправке — support:write; кредиты не тратит ни то, ни другое.

curl -s https://api.coremind.ru/v1/support/messages \
  -H "Authorization: Bearer cmk_..."

curl -s -X POST https://api.coremind.ru/v1/support/messages \
  -H "Authorization: Bearer cmk_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "...", "client_msg_id": "<uuid>"}'

Инструкция для агента

Чтобы код против API писал Codex или Claude Code, вставьте блок ниже в AGENTS.md (Codex) или CLAUDE.md (Claude Code) в корне проекта. Он направляет агента на llms.txt — оглавление документации для агентов: это соглашение, и сами ни Codex, ни Claude Code его не ищут. Если агент должен не писать код, а генерировать сам, ему нужен не этот блок, а MCP-сервер.

## CoreMind API

Перед тем как писать код, прочитай:
- https://coremind.ru/llms.txt — оглавление документации
- https://coremind.ru/docs/api/openapi.json — спецификация OpenAPI: маршруты, поля, коды ответов

Правила:
- Адрес сервера https://api.coremind.ru, пути маршрутов уже начинаются с /v1. Каждый запрос — с заголовком Authorization: Bearer <ключ>. Ключ бери из переменной окружения COREMIND_API_KEY и никогда не записывай в файлы.
- Модели и допустимые значения полей бери из GET /v1/models, не придумывай.
- Перед POST /v1/generations вызывай POST /v1/pricing/estimate с тем же телом и показывай цену пользователю до траты кредитов. Если оценка ответила 422 с кодом estimate_unavailable (так отвечает запрос с promo_mode), цену заранее узнать нельзя: скажи об этом пользователю и отправляй только с его согласия.
- POST /v1/generations отправляй с заголовком Idempotency-Key: новый UUID на каждую новую генерацию, тот же ключ с тем же телом — на повтор после сбоя сети.
- Смену статуса генераций получай из потока GET /v1/events, а запись читай через GET /v1/generations/{id}. POST /v1/generations/status — только чтобы добрать пропущенное после обрыва потока или кадра reset, не для опроса.
- При ошибке ветвись по error.code, а не по тексту и не по одному HTTP-статусу. Что значит каждый код и что делать дальше, описано в https://coremind.ru/docs/api/error-codes.

Что дальше

  • Аутентификация — свойства ключа и словарь областей доступа.
  • OAuth для приложений — доступ от имени другого пользователя вместо ключа.
  • Идемпотентность — обязательный Idempotency-Key на отправке генерации и повторы на остальных записывающих маршрутах.
  • События (SSE) — поток смены статусов, переподключение по Last-Event-ID и его лимиты.
  • Лимиты и квоты — частота запросов, размер страницы, кеширование.
  • Коды ошибок — по чему ветвиться, когда вызов не прошёл.
  • Справочник — полная спецификация всех маршрутов и схем.
  • MCP-сервер — чтобы агент генерировал сам, без кода против API.