Memo AIDocs
API Reference

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

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

POST /transcriptions

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

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

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

Запрос

Минимальный рабочий вызов: ключ, имя файла и сами байты. Полный список заголовков — в Формате запроса, необязательные параметры — в Query-параметрах.

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 — файл принят и поставлен в очередь. Расшифровка идет асинхронно.

{
  "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "queued",
  "reports": []
}
ПолеТипОписание
uuidUUIDИспользуйте с Получением расшифровки для опроса и результата.
statusstringПри успехе всегда queued.
reports[]arrayAI-отчеты, заказанные через ?reports=, как { id, slug, status } — здесь status всегда queued. Пусто, если отчеты не запрашивались. См. AI-отчеты.

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

Опрашивайте 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 — загрузите заново.

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

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

ЧастьЗначение
ТелоБайты файла как есть (--data-binary в curl).
AuthorizationBearer mk_live_… (ключ с правом загрузки).
Content-LengthОбязателен. HTTP-клиенты ставят его сами для файловых тел; запрос без него отклоняется с кодом 411.
Content-DispositionОбязателен: attachment; filename="meeting.mp3". Расширение определяет формат, имя становится названием расшифровки.
Idempotency-KeyНеобязателен, но рекомендуем: уникальная строка на файл (например, UUID). Делает ретраи безопасными — см. Ретраи.
X-External-IdНеобязателен. Ваш идентификатор — id сделки в CRM, id звонка в телефонии. До 128 печатаемых ASCII-символов. Возвращается во всех ответах, см. Свои идентификаторы.
X-MetadataНеобязателен. JSON-объект с вашими данными, до 4096 байт. Возвращается вместе с расшифровкой.
X-AuthorНеобязателен. Кому из участников workspace принадлежит расшифровка — email:name@company.com или user:<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-слой: саммари, название, имена спикеров и темы. По умолчанию false — см. ниже. AI-отчеты отдельно — запрашивайте через reports.
reportsstringИдентификаторы отчетов через запятую — они сгенерируются, как только расшифровка будет готова, например reports=summary,tasks. До 3 на загрузку; нужен ключ с правом Разрешить AI-отчеты. Идентификаторы — в Списке отчетов.

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

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

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

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.

Свои идентификаторы

Расшифровка почти всегда относится к чему-то в вашей системе: к сделке, к звонку, к заявке. Передайте свой идентификатор при загрузке — и получите его обратно везде, вместо того чтобы держать у себя таблицу «наш id ↔ ваш».

curl -X POST 'https://app.memoai.tech/api/v1/developer/transcriptions' \
  -H 'Authorization: Bearer mk_live_your_key_here' \
  -H 'Content-Disposition: attachment; filename="call.mp3"' \
  -H 'X-External-Id: deal-A-1043' \
  -H 'X-Metadata: {"crm":"bitrix24","manager_id":17,"queue":"sales"}' \
  --data-binary @call.mp3

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

Что можно передавать

ЗаголовокПравила
X-External-IdДо 128 символов, печатаемый ASCII без переводов строк. Уникальным быть не обязан.
X-MetadataJSON-объект, до 4096 байт. Ключи — до 64 символов. Значения — строка, число, true/false, null или массив из них. Вложенность до 3 уровней.

Не подошедшее значение отклоняется при загрузке с кодом 422, а code в ответе говорит, что именно не так: external_id_too_long, metadata_too_deep, metadata_value_invalid и так далее. Молча обрезать или игнорировать мы ничего не будем.

Что приходит обратно

Оба поля есть в GET /transcriptions/{id} и в каждом элементе списка:

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "external_id": "deal-A-1043",
  "metadata": { "crm": "bitrix24", "manager_id": 17, "queue": "sales" },
  "status": "completed"
}

У расшифровок, загруженных не через API, оба поля равны null — это не ошибка.

Поиск по своим идентификаторам

Наш id знать не нужно: список фильтруется по вашим значениям.

GET /transcriptions?external_id=deal-A-1043
GET /transcriptions?metadata.queue=sales

Сравнение точное, по одному ключу за раз; это не полнотекстовый поиск. Фильтры складываются с остальными — например, ?metadata.queue=sales&status=completed.

Один идентификатор на нескольких расшифровках

X-External-Id не обязан быть уникальным. Одна сделка обычно означает несколько звонков, и все они могут нести один и тот же deal-A-1043 — тогда ?external_id=deal-A-1043 вернёт их все.

Если нужно защититься от повторной загрузки одного и того же файла, для этого есть Idempotency-Key с окном 24 часа — см. Ретраи.

Не кладите сюда персональные данные и секреты

В отличие от расшифровки, эти два поля не шифруются: по ним работает поиск, а искать по зашифрованному нельзя. Они хранятся так, как вы их прислали.

Держите здесь идентификаторы и признаки — deal-A-1043, queue: sales, — а не имена, телефоны и ключи. Значения, похожие на токены и ключи доступа, отклоняются при загрузке, но это подстраховка, а не гарантия.

Назначение автора

По умолчанию расшифровка, загруженная через API, принадлежит тому, кто создал ключ. Для команды это почти всегда не то: интеграция с CRM или телефонией загружает звонки разных менеджеров, и каждый должен видеть свои звонки в разделе «Мои встречи», получать уведомление о готовности и попадать в отчеты по людям.

Передайте владельца в заголовке X-Author. Значение типизировано — <type>:<value>:

ЗначениеЧто означает
email:name@company.comУчастник workspace по email, с которым он зарегистрирован. Регистр не важен. Вариант без настройки: email менеджеров есть в любой CRM.
user:9c2d4e6f-1a2b-4c3d-8e9f-0a1b2c3d4e5fУчастник workspace по id — тот author.id, который приходит в ответах, и items[].id в Участниках. Это UUID, а не число.
external:<provider>:<id>Ваш собственный идентификатор человека — id пользователя в CRM, внутренний номер в телефонии: external:amocrm:12345, external:mango:ext-101. Работает, когда идентификатор привязан к участнику: владелец workspace делает это в приложении в разделе Настройки → Участники → Внешние идентификаторы, а интеграция — через Участники и идентификаторы. provider — произвольная метка строчными буквами на ваш выбор; id сравнивается точно.
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="call-1043.mp3"' \
  -H 'X-Author: email:ivanov@company.com' \
  -H 'X-External-Id: deal-A-1043' \
  --data-binary @call-1043.mp3

Что получает автор, а что остается за ключом:

  • Расшифровка принадлежит автору: она в его списке, ему приходит уведомление о готовности, в отчетах по команде она считается его. Каждый ответ возвращает владельца как author: { id, email, name }.
  • Права, лимиты и списание — ключа. Проект, к которому вы привязываете запись, должен быть доступен создателю ключа; минуты списываются с workspace как обычно.
  • Автор не обязан состоять в проекте, к которому вы привязали запись. Он все равно ее увидит — она его, — а администратор добавит его в проект позже. Загрузка из-за этого не отклоняется, автоматизация продолжает работать.

Если автора найти не удалось, загрузка отклоняется с 422 author_not_member, и ничего не списывается — тихого перехода на создателя ключа нет: неверно приписанный звонок заметить труднее, чем упавший. Код один и тот же и для незнакомого нам email, и для человека вне вашего workspace. Пригласите его в workspace или загрузите без X-Author и назначьте автора позже.

Маппинг менеджеров CRM

Почти любая CRM хранит email менеджера, так что для email: таблица соответствий обычно не нужна: отправляйте email пользователя CRM, а на author_not_member показывайте в настройках «этот менеджер еще не в workspace». Передавайте в том же запросе X-External-Id, чтобы расшифровка была связана и со сделкой или звонком.

Ретраи

Без предосторожностей повтор загрузки, ответ на которую вы не увидели (соединение оборвалось после последнего байта), создал бы вторую расшифровку и второе списание. Заголовок 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.

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

ЛимитЗначение
Размер файла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 — повторяйте с джиттером.
СписаниеОбычные минуты тарифа, как при загрузке в приложении. Минуты резервируются при старте обработки.

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

Ошибки

СтатусКодКогда
400unsupported_file_format / file_too_small / filename_requiredНеподдерживаемое расширение, < 1 КБ или нет Content-Disposition.
402insufficient_balanceМинуты тарифа закончились.
403upload_scope_requiredУ ключа нет права загрузки.
403reports_scope_requiredПередан ?reports=, но у ключа нет права Разрешить AI-отчеты.
422prompt_not_found / too_many_reportsИдентификатор из ?reports= неизвестен владельцу ключа (detail.slug уточняет, какой) или запрошено больше 3. Файл не загружается.
422external_id_invalid / metadata_*Невалидный X-External-Id или X-Metadata — код уточняет причину.
422invalid_author / unsupported_author_type / author_not_memberX-Author не разобрался, использует пока неподдерживаемый тип или называет человека, который не является активным участником workspace. См. Назначение автора.
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.
429daily_reports_quota_exceeded / too_many_queued_reportsКапы на AI-отчеты (300 на workspace в сутки, 200 ожидающих на workspace) — уважайте Retry-After.

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

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