Memo AIDocs
API Reference

Загрузка файла

POST /transcriptions — загрузите аудио или видео (до 2 ГБ) на расшифровку одним HTTP-запросом и опрашивайте статус до completed.

POST /transcriptions

Загружает один аудио/видеофайл сырым телом запроса и запускает расшифровку. Отвечает сразу, возвращая id расшифровки — дальше опрашивайте Получение расшифровки, пока status не станет completed.

Нужно разрешение на загрузку

Эндпоинт доступен только ключам, созданным с включенной опцией Разрешить загрузку файлов. Существующие ключи — только для чтения: если ваш ключ старше этой функции, создайте новый. См. Аутентификация → Права ключа.

Формат запроса

Файл — это тело запроса. Без multipart-форм, без JSON-оберток, без протоколов чанков:

ЧастьЗначение
ТелоБайты файла как есть (--data-binary в curl).
AuthorizationBearer mk_live_… (ключ с правом загрузки).
Content-LengthОбязателен. HTTP-клиенты ставят его сами для файловых тел; запрос без него отклоняется с кодом 411.
Content-DispositionОбязателен: attachment; filename="meeting.mp3". Расширение определяет формат, имя становится названием расшифровки.
Idempotency-KeyНеобязателен, но рекомендуем: уникальная строка на файл (например, UUID). Делает ретраи безопасными — см. Ретраи.

Почему имя файла — в заголовке

В именах файлов часто есть персональные данные. Заголовки не попадают в логи URL — поэтому ?filename= не принимается. Для кириллицы используйте стандартную форму RFC 5987: Content-Disposition: attachment; filename*=UTF-8''%D0%9F%D0%BB%D0%B0%D0%BD.mp3.

Query-параметры

Все необязательные:

ПараметрТипОписание
languagestringЯзык речи. По умолчанию auto (определяется по аудио) — см. Поддерживаемые языки.
project_idUUIDПрикрепить расшифровку к проекту. UUID — из Списка проектов.
diarizebooleanОпределять спикеров. По умолчанию true.
speakers_countintegerЧисло спикеров (120), если известно — улучшает диаризацию.
ai_metadatabooleanГенерировать AI-слой: саммари, название, имена спикеров, темы и дефолтные AI-отчеты. По умолчанию false — см. ниже.

AI-анализ по умолчанию выключен для API-загрузок

С ai_metadata=false (по умолчанию) вы быстрее получаете полный текст: text с метками спикеров, но summary и topics остаются null, prompt_results пуст, название — имя файла, спикеры под генерик-метками. Передайте ai_metadata=true, если нужен тот же AI-слой, что делает приложение. В обоих случаях расшифровка появляется в приложении, и AI-отчеты можно сгенерировать там вручную позже. Списание минут одинаковое в обоих режимах.

Поддерживаемые языки

auto (по умолчанию — язык определяется по аудио) или один из:

en, es, ru, zh, hi, ar, pt, bn, fr, de, ja, ko, tr, it, vi, pl, uk, nl, id, th, fa, sv, cs, ro, el, hu, da, fi, no, sk, he, ms, bg, hr, sr, sl, lt, lv, et, ta, te, mr, ur, sw, ml, kn, gu, pa, my, ne, si, km, lo, az, kk, uz, ka, hy, sq, bs, mk, is, mn, tg, tk, tt, ca, eu, gl, cy, af, tl, jw, su, yo, ha, so, am, sn, mg, ln, mt, lb, oc, br, nn, fo, sd, ps, ht, mi, haw, yi, ba, be, as, bo, sa, la

Двухбуквенные коды ISO 639-1 (плюс haw и jw). Неподдерживаемое значение вернет 422.

Запрос

curl -X POST "https://app.memoai.tech/api/v1/developer/transcriptions?language=ru" \
  -H "Authorization: Bearer mk_live_your_key_here" \
  -H 'Content-Disposition: attachment; filename="standup.mp3"' \
  --data-binary @standup.mp3
import httpx

with open("standup.mp3", "rb") as f:
    resp = httpx.post(
        "https://app.memoai.tech/api/v1/developer/transcriptions",
        params={"language": "ru"},
        headers={
            "Authorization": "Bearer mk_live_your_key_here",
            "Content-Disposition": 'attachment; filename="standup.mp3"',
        },
        content=f,        # стримится с диска — работает и для 2 ГБ
        timeout=httpx.Timeout(10, write=None),  # не обрывать долгую отправку
    )
resp.raise_for_status()
t = resp.json()
print(t["uuid"], t["status"])   # "3fa85f64-…" "queued"
import { openAsBlob } from "node:fs"; // Node 20+

const res = await fetch(
  "https://app.memoai.tech/api/v1/developer/transcriptions?language=ru",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer mk_live_your_key_here",
      "Content-Disposition": 'attachment; filename="standup.mp3"',
    },
    body: await openAsBlob("standup.mp3"), // стрим, Content-Length выставится сам
  },
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const t = await res.json();
console.log(t.uuid, t.status); // "3fa85f64-…" "queued"

С подключенным локальным MCP-сервером просто попросите:

Загрузи ~/Записи/standup.mp3 в Memo и дай список задач.

Ассистент вызовет memo_upload_file, а затем будет опрашивать memo_get_transcription, пока расшифровка не будет готова. Нужен ключ с правом загрузки; работает только с локальным сервером — удаленный коннектор не имеет доступа к файлам на вашем диске.

Ответ

201 Created — файл принят и поставлен в очередь. Расшифровка идет асинхронно.

{
  "id": 12345,
  "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "queued"
}
ПолеТипОписание
idintegerВнутренний числовой id.
uuidUUIDИспользуйте с Получением расшифровки для опроса и результата.
statusstringПри успехе всегда queued.

Статусы и опрос

Опрашивайте GET /transcriptions/{uuid} раз в 5–10 секунд до терминального статуса:

statusЗначение
queuedПринято; ждет слот обработки.
processingРасшифровка идет.
completedГотово — в ответе появились text и prompt_results. Терминальный.
failedНе удалось обработать — см. error_code ниже. Терминальный.
insufficient_balanceНе хватило минут тарифа. Файл не сохраняется — пополните баланс и загрузите заново. Терминальный.
import time

while True:
    t = httpx.get(f"{BASE}/transcriptions/{uuid}", headers=HEADERS).json()
    if t["status"] in ("completed", "failed", "insufficient_balance"):
        break
    time.sleep(10)

Частые значения error_code при failed:

КодЗначение
FILE_CORRUPTEDФайл не читается как медиа (не те байты, битый контейнер).
FILE_NO_AUDIOНет аудиодорожки (например, видео без звука).
RECORDING_TOO_LONGДлиннее 10 часов.
UPLOAD_INCOMPLETEБайтов пришло меньше, чем обещал Content-Length — загрузите заново.

Лимиты и списание

ЛимитЗначение
Размер файла1 КБ – 2 ГБ, на любом тарифе с доступом к API.
Длительность1 секунда – 10 часов.
ФорматыАудио: mp3, wav, m4a, aac, ogg, flac, opus, amr, wma, aiff и другие. Видео: mp4, mov, mkv, avi, webm, mpeg и другие — звук извлекается автоматически.
ПараллельностьФайлы обрабатываются параллельно по вашему тарифу — лишние не отклоняются, ждут в статусе queued и стартуют сами. Одновременных заливок отдельный лимит (10 на ключ, 16 на workspace); сверх него 429 — повторяйте с джиттером.
СписаниеОбычные минуты тарифа, как при загрузке в приложении. Минуты резервируются при старте обработки.

Анти-абьюз ограничители (суточные капы на ключ, лимит одновременных загрузок) выставлены сильно выше нормального использования — если получили 429 с upload_quota_exceeded, напишите в поддержку, поднимем.

Ретраи

Без предосторожностей повтор загрузки, ответ на которую вы не увидели (соединение оборвалось после последнего байта), создал бы вторую расшифровку и второе списание. Заголовок Idempotency-Key решает это:

  • Передавайте уникальное значение на файл (UUID идеален) с каждой попыткой.
  • Если файл уже принят под этим ключом, придет 200 с оригинальным uuid вместо дубля — без двойного списания, квоты не тронуты.
  • Запрос с тем же ключом, который еще в полете, вернет 409 idempotency_conflict — подождите Retry-After и повторите.
  • Добавляйте джиттер: спите случайную долю Retry-After, а не ровно указанное время. Если лимит поймали сразу много клиентов, одновременное пробуждение просто воссоздаст очередь.
  • Запоминается только успех (на 24 часа); после неудачной попытки ключ свободен для повтора.
curl -X POST "https://app.memoai.tech/api/v1/developer/transcriptions" \
  -H "Authorization: Bearer mk_live_your_key_here" \
  -H 'Content-Disposition: attachment; filename="standup.mp3"' \
  -H "Idempotency-Key: 7f3a2b1c-9d4e-4f5a-8b6c-1d2e3f4a5b6c" \
  --data-binary @standup.mp3

Без Idempotency-Key повторяйте только при ошибках

Если заголовок не передаете — никогда не шлите файл повторно после 201; повторяйте только при сетевых сбоях, 429 (после паузы из Retry-After) и 5xx.

Ошибки

СтатусКодКогда
400unsupported_file_format / file_too_small / filename_requiredНеподдерживаемое расширение, < 1 КБ или нет Content-Disposition.
402insufficient_balanceМинуты тарифа закончились.
403upload_scope_requiredУ ключа нет права загрузки.
408upload_timeoutЗагрузка зависла (нет данных 60 с) или шла дольше 90 минут.
411length_requiredНет Content-Length.
413file_size_limit_exceededБольше 2 ГБ.
429too_many_concurrent_uploads / too_many_queued_files / upload_quota_exceededЛимит параллельности, очереди или суточный кап — уважайте Retry-After.

Полный справочник и правила ретраев — в разделе Ошибки.

На этой странице