Загрузка звонков и чатов
Есть два способа загрузить запись звонка (или текст чата) и создать из неё запись в Aelo — какой использовать, зависит от того, где уже находится ваше аудио.
| Эндпоинт | Тело | Используйте, когда… |
|---|---|---|
POST /records | JSON | У вас есть fileUrl, по которому Aelo может скачать файл (или текст чата) |
POST /records/upload | multipart/form-data | Аудио уже у вас на руках, и разместить его негде |
Оба эндпоинта требуют скоуп records:write.
Оба также требуют, чтобы целевой проект был в статусе active. Проект в
статусе paused или archived отвечает project_not_accepting_records
(409) вместо приёма записи — прежде чем projectId снова сможет принимать
записи, администратор организации должен реактивировать проект.
Лимиты размера
Section titled “Лимиты размера”| Путь | Лимит | Почему |
|---|---|---|
POST /records (fileUrl) | 100 МБ, либо 25 МБ, если источник не сообщает Content-Length | Aelo скачивает файл потоком напрямую в хранилище; источник, не сообщающий длину, потоком передать нельзя — он держится в памяти |
POST /records (JSON-тело / textContent) | 10 МБ | Всё JSON-тело читается в память целиком до валидации, поэтому потолок совпадает с multipart-лимитом для чата ниже |
POST /records/upload (multipart) | 25 МБ | Multipart-тело нужно полностью буферизовать в памяти перед чтением, поэтому потолок намного ниже |
Если ваш файл больше 25 МБ, разместите его там, куда Aelo может обратиться по
HTTPS, и вызовите POST /records с fileUrl вместо передачи самих байтов.
Загрузка по URL
Section titled “Загрузка по URL”curl -X POST https://api.aelo.cloud/api/public/v1/records \ -H "Authorization: Bearer aelo_sk_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "projectId": "your-project-id", "type": "call", "externalId": "crm-call-12345", "fileUrl": "https://your-storage.example.com/call.mp3", "agentId": "42", "agentName": "Ivan Petrov", "durationSeconds": 184, "callDirection": "outbound", "crmEntityType": "deal", "crmEntityId": "9001" }'fileUrl обязан быть https:// и указывать на публичный адрес — Aelo
отклоняет URL, ведущие в приватные или link-local сети. Aelo ждёт скачивание
до 30 секунд; более медленный или недоступный источник вернётся как
fetch_failed (проблема в самом URL) или resolution_unavailable (проблема на
стороне резолвера Aelo, временная — повторите запрос).
Запрос обязан объявить Content-Length. Тело с chunked-кодировкой
(Transfer-Encoding: chunked) отклоняется с 400 ещё до попытки разбора — то
же правило, что и для multipart-загрузки ниже. Это касается всего запроса, а
не только type: "chat": тело с fileUrl крошечное и никогда не приближается
к лимиту в 10 МБ, но проверка выполняется раньше, чем Aelo узнаёт, какая это
ветка.
type — это "call" или "chat":
type: "call"требуетfileUrlи отклоняетtextContent.type: "chat"требуетtextContent(текст транскрипта) и отклоняетfileUrl.
Загрузка файла напрямую
Section titled “Загрузка файла напрямую”curl -X POST https://api.aelo.cloud/api/public/v1/records/upload \ -H "Authorization: Bearer aelo_sk_YOUR_KEY" \ -F "file=@call.mp3" \ -F "projectId=your-project-id" \ -F "externalId=crm-call-12345" \ -F "agentId=42" \ -F "agentName=Ivan Petrov" \ -F "callDirection=outbound"Аудио передаётся в части с именем file; все остальные части — поля
метаданных под теми же именами, что и в JSON-теле (значения приходят строками
и приводятся к нужному типу там, где нужно — например, durationSeconds).
Так можно загружать только звонки: type по умолчанию равен "call", а
type=chat отклоняется — отправьте текст чата на POST /records с
textContent. Если в форме случайно оказалась вторая часть с именем file,
прочитана будет только первая — остальные молча игнорируются.
Запрос обязан объявить Content-Length. Тело с chunked-кодировкой
(Transfer-Encoding: chunked) отклоняется с 400 ещё до попытки разбора. Любой
обычный клиент выставляет этот заголовок автоматически — curl -F, fetch с
телом File/Blob, multipart-хелпер любого SDK, — так что это затрагивает
только запрос, вручную собранный поверх ReadableStream.
Обязательные и необязательные поля
Section titled “Обязательные и необязательные поля”| Поле | Обязательно | Примечание |
|---|---|---|
projectId | Да | Пробелы по краям обрезаются перед поиском; значение только из пробелов отклоняется |
type | JSON: да · multipart: нет | call или chat; в multipart по умолчанию call и chat не принимается |
fileUrl | JSON, только звонки | Не принимается для type: "chat" |
textContent | JSON, только чаты | Не принимается для type: "call" |
externalId | Нет | Ваш собственный id записи — см. Идемпотентность ниже |
occurredAt | Нет | ISO-8601. Время звонка по бизнес-часам — см. примечание про дашборды ниже. Отвергается раньше 2000-01-01 и дальше чем на 24 ч в будущее |
agentId | Нет | Произвольный формат: id из CRM обычно числовая строка, id из телефонии — id оператора. См. Предупреждения |
agentName, participantName, participantPhone, agentPhone | Нет | |
durationSeconds | Нет | Не передавайте, если значения нет — см. Предупреждения. 0–86400 (24 ч); вне диапазона запрос отвергается |
callDirection | Нет | inbound или outbound |
language | Нет | 2–8 символов |
crmEntityType | Нет | deal, lead, contact или company |
crmEntityId, crmEntityName, crmActivityName | Нет |
Любое нераспознанное поле этот API отклоняет с invalid_request, а не
отбрасывает молча — опечатка вроде projectID даёт явную ошибку, а не тихую
потерю данных.
Пустое необязательное поле означает «не отправлено». Если поле формы
приходит пустым или состоящим только из пробелов (-F "agentId=", или
неустановленная переменная окружения, подставленная в форму), Aelo трактует
это как отсутствие значения, а не как пустую строку — включая externalId и
agentId. С обязательным полем всё иначе: пустое значение — это 400, оно
не считается «не отправленным». Это различие касается только multipart-тел;
JSON "" — значение, введённое намеренно, и обрабатывается как есть.
Идемпотентность
Section titled “Идемпотентность”externalId — ваш собственный id записи: id активности из CRM, id звонка из
телефонии — что уже используется в вашей системе. Отправьте тот же externalId
повторно, и Aelo вернёт существующую запись вместо создания дубликата (200
вместо 201).
Уникальность действует в рамках организации, а не проекта. Если вы
отправите один и тот же externalId под двумя разными projectId в одной
организации, второй вызов не создаст вторую запись — он вернёт запись
первого проекта. Если ваша интеграция переиспользует id звонков между
разными проектами, либо делайте их уникальными на своей стороне, либо
закладывайтесь на то, что вторая загрузка «схлопнется» в запись первого
проекта.
Предупреждения
Section titled “Предупреждения”Успешная загрузка (200 или 201) всё равно может содержать warnings —
запись создана, но часть аналитики её не увидит:
| Код | Когда | Сообщение |
|---|---|---|
missing_agent_id | Нет agentId | «No agentId supplied: this record is excluded from agent ratings (they filter on agent_id IS NOT NULL).» |
missing_crm_entity | Нет пары crmEntityType/crmEntityId | «No crmEntityType/crmEntityId pair: this record joins no call series, so it appears in no funnel or deal outcome.» |
missing_duration | type: "call" без durationSeconds | «No durationSeconds: leaderboard totals and short-call detection ignore this record.» |
warnings появляется только в ответах обоих эндпоинтов загрузки — этого поля
нет в формате записи, который возвращают GET-эндпоинты. Пустое значение
durationSeconds в multipart-форме трактуется так же, как его отсутствие: Aelo
никогда не сохранит его как 0, и предупреждение missing_duration всё равно
сработает.
Дашборды группируют по времени загрузки
Section titled “Дашборды группируют по времени загрузки”Aelo сохраняет occurredAt в записи, но дашборды и отчёты в v1 группируют
звонки по времени, когда Aelo их получила (createdAt), а не по occurredAt.
Если вы задним числом загружаете исторические звонки с occurredAt недельной
или месячной давности, они попадут в сегодняшние цифры, а не в тот период,
когда произошли на самом деле. Поэтому массовый исторический импорт в v1
не поддерживается — этот API предназначен для загрузки звонков и чатов по
мере их появления.
Смотрите также
Section titled “Смотрите также”- Обзор публичного API — аутентификация, эндпоинты чтения и полная таблица ошибок