# KolWrite API для ИИ-агентов

Как позволить ИИ-агенту (Claude Code, Codex, Cursor или вашему собственному скрипту)
превращать записи в расшифровки, субтитры SRT и тайминги слов через API платформы
KolWrite по адресу `https://api.kolwrite.com`. Человек один раз создаёт токен; агент
читает его из переменной окружения и вызывает обычный HTTPS. SDK не нужен.

Это API платформы с токенами `kol_`. API кредитов на `https://app.kolwrite.com`
(заголовок `x-api-key`) — другое API, оно описано (по-английски) на
https://console.kolwrite.com/llms.txt для тех, кто ищет его.

## 1. Выдайте агенту токен

- Доступ к API включается для каждого аккаунта отдельно; без него создание и использование токена отклоняются.
- Человек создаёт токен в приложении KolWrite (**Настройки → Аккаунт → Доступ к
  API → Создать токен**) и выбирает минимальный набор областей доступа. Секрет
  (`kol_...`) показывается один раз.
- Храните его в переменной окружения, вдали от промптов, журналов и git.

| Задача | Области доступа |
|---|---|
| Расшифровка и экспорт | `files:write`, `jobs:write`, `jobs:read`, `transcripts:read` |
| Плюс проверка баланса кредитов | добавьте `billing:read` |
| Эндпоинты, совместимые с OpenAI | `ai:use` (тратит кредиты; не входит в набор Automation в приложении) |
| Создание ссылок для общего доступа | добавьте `shares:write` |

Вставьте это в `AGENTS.md`, `CLAUDE.md` или `.cursorrules`:

```
## KolWrite platform API
- Base URL https://api.kolwrite.com. The token is in the environment variable KOLWRITE_TOKEN: send it as "Authorization: Bearer $KOLWRITE_TOKEN" and never print it.
- Flow: POST /api/v1/media/upload-session, PUT the bytes to data.upload_url, POST /api/v1/media/complete, POST /api/v1/jobs, poll GET /api/v1/jobs/{job_id} until status is succeeded, then GET /api/v1/jobs/{job_id}/export?format=json
- Spec: https://kolwrite-docs.pages.dev/openapi.json. Guide: https://kolwrite-docs.pages.dev/agents.md
```

## 2. Какой вызов использовать

| Вам нужно | Вызов |
|---|---|
| Расшифровка файла с таймингами слов и говорящими | сессия загрузки, затем `POST /api/v1/jobs` (`type: transcription`), затем `GET /api/v1/jobs/{job_id}/export?format=json` |
| Субтитры SRT или VTT | `GET /api/v1/jobs/{job_id}/export?format=srt` или `format=vtt` (тариф Plus или выше) |
| Субтитры на другом языке | `options.target_language` в задаче, затем экспорт |
| Обычный текст | `export?format=text` |
| Текст короткой голосовой записи без задачи | `POST /api/v1/ai/openai/audio/transcriptions` |
| Чат, ответы, эмбеддинги | `/api/v1/ai/openai/*` ([эндпоинты, совместимые с OpenAI](/locales/ru/openai-compatible.md)) |
| Сохранённая расшифровка с версиями | `GET /api/v1/jobs/{job_id}/artifacts` |
| Баланс кредитов | `GET /api/v1/billing/balance` |
| Публичная ссылка на расшифровку | `POST /api/v1/shares` |
| Какие профили моделей существуют | `GET /api/v1/models` |

## 3. Расшифровка файла на Python

```python
import json, mimetypes, os, time
import requests

BASE = "https://api.kolwrite.com"
TOKEN = os.environ["KOLWRITE_TOKEN"]


def call(method, path, **kw):
    headers = {"Authorization": f"Bearer {TOKEN}", **kw.pop("headers", {})}
    r = requests.request(method, BASE + path, headers=headers, timeout=120, **kw)
    r.raise_for_status()
    return r


def transcribe(path, profile="oracle", language="auto"):
    name, size = os.path.basename(path), os.path.getsize(path)
    ctype = mimetypes.guess_type(name)[0] or "application/octet-stream"
    session = call("POST", "/api/v1/media/upload-session",
                   json={"filename": name, "file_size_bytes": size,
                         "content_type": ctype}).json()["data"]
    with open(path, "rb") as f:
        requests.put(session["upload_url"], data=f, headers=session["headers"],
                     timeout=3600).raise_for_status()
    media = call("POST", "/api/v1/media/complete",
                 json={"finalize_token": session["finalize_token"]}).json()["data"]
    job = call("POST", "/api/v1/jobs",
               headers={"Idempotency-Key": f"{name}-{size}-{profile}-{language}"},
               json={"type": "transcription",
                     "input": {"type": "media_ref", "media_id": media["media_id"]},
                     "profile": profile,
                     "options": {"language": language,
                                 "diarization": {"mode": "regular"}}}).json()["data"]
    while True:
        status = call("GET", f"/api/v1/jobs/{job['job_id']}").json()["data"]["status"]
        if status in ("succeeded", "failed", "cancelled"):
            break
        time.sleep(5)
    if status != "succeeded":
        raise RuntimeError(f"job {job['job_id']} {status}")
    export = call("GET", f"/api/v1/jobs/{job['job_id']}/export",
                  params={"format": "json"}).json()
    return job["job_id"], json.loads(export["data"])  # data is a JSON string


job_id, transcript = transcribe("interview.wav")
for seg in transcript["segments"]:
    print(f"{seg['start']:.2f}-{seg['end']:.2f} {seg.get('speaker', '')} {seg['text']}")
```

Большие файлы загружаются частями: тогда сессия возвращает `data.upload_url`
со значением `null` и по одному URL на каждую часть в `data.multipart_part_urls`
(см. [быстрый старт](/locales/ru/quickstart.md)).

## 4. Используйте тайминги

В JSON-экспорте есть `segments`; у каждого сегмента `start` и `end` в секундах, `text`,
`speaker`, если выполнялось разделение по говорящим, и `words` (`text`, `start`, `end`).
Пусть агент выбирает сегменты по номеру и берёт время из расшифровки; никогда не
позволяйте модели вписывать временные метки самой.

```python
import subprocess

keep = [s for s in transcript["segments"] if "pricing" in s["text"].lower()]
for i, seg in enumerate(keep):
    subprocess.run(["ffmpeg", "-y", "-i", "talk.mp4", "-ss", str(seg["start"]),
                    "-to", str(seg["end"]), f"clip{i}.mp4"], check=True)
```

Субтитры приходят прямо из API. `include_speakers=false` убирает имена говорящих,
а `max_chars_per_entry` задаёт длину каждой строки субтитров:

```bash
curl -H "Authorization: Bearer $KOLWRITE_TOKEN" -o talk.srt \
  "https://api.kolwrite.com/api/v1/jobs/JOB_ID/export?format=srt&include_speakers=false&max_chars_per_entry=42"
```

Скачивание SRT и VTT требует тарифа Plus или выше. На других тарифах возвращается
HTTP 403 с кодом `subtitle_export_requires_plus`; экспорт в JSON и текст это не затрагивает.

## 5. Правила для агентов

- Передавайте `Idempotency-Key` при создании задачи, чтобы повторный запрос не запустил вторую.
- Повторяйте запрос после `429`, `502`, `503` и `504` с экспоненциальной задержкой; при `429`
  дождитесь `Retry-After`. Не повторяйте после `401`, `403` и ошибок валидации. См.
  [ошибки](/locales/ru/errors.md).
- Читайте `detail.code`, а не текст сообщения.
- Задача резервирует кредиты при создании и рассчитывается по завершении; проверьте
  `GET /api/v1/billing/balance` перед большой партией.
- Файл OpenAPI — это контракт: каждая операция указывает нужную область доступа в
  `x-required-scope`.

## Вопросы

### Как сделать субтитры SRT из видео с помощью ИИ-агента?

Загрузите файл, создайте задачу расшифровки, дождитесь `succeeded` и вызовите
эндпоинт экспорта с `format=srt`. Для субтитров нужен тариф Plus или выше.

### Могут ли Claude Code или Codex вызывать API KolWrite?

Да. Они вызывают его по HTTPS с персональным токеном доступа из переменной окружения.
Устанавливать SDK или MCP-сервер не нужно.

### Как агент получает временные метки каждого слова?

Экспортируйте задачу в JSON. У каждого сегмента есть `words` с `start` и `end` в секундах.
