Завантаження дзвінків і чатів
Є два способи завантажити запис дзвінка (або текст чату) і створити з нього запис в 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 — автентифікація, ендпоінти читання і повна таблиця помилок