API Reference

Техническая документация для интеграции с CallMeAI

API Reference

Техническая документация для разработчиков внешних систем, интегрирующихся с CallMeAI.

Базовый URL

https://callmeai.ru/api/orpc

Все endpoints доступны через POST-запросы к этому базовому URL.


Аутентификация

Все запросы требуют Bearer-токен в заголовке Authorization:

Authorization: Bearer mai_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Получить токен можно в настройках API токенов. Подробнее: Документация API токенов.


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

  • Метод: POST
  • Content-Type: application/json (для большинства endpoints)
  • Content-Type: multipart/form-data (для загрузки файлов)

Тело запроса

Все параметры передаются в JSON-формате:

{
  "where": { ... },
  "include": { ... },
  "take": 10,
  "skip": 0
}

Формат ответов

Успешный ответ

{
  "result": { ... }
}

Ошибка

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "API token has expired"
  }
}

Параметры запросов

Все endpoints используют стандартные параметры Prisma для фильтрации, сортировки и пагинации.

where — фильтрация

Объект where позволяет фильтровать записи по полям. Поддерживаются операторы сравнения:

ОператорОписаниеПример
equalsТочное совпадение{ "id": 1 } или { "id": { "equals": 1 } }
notНе равно{ "id": { "not": 1 } }
inВходит в список{ "id": { "in": [1, 2, 3] } }
notInНе входит в список{ "id": { "notIn": [1, 2] } }
ltМеньше{ "duration": { "lt": 5 } }
lteМеньше или равно{ "duration": { "lte": 5 } }
gtБольше{ "duration": { "gt": 2 } }
gteБольше или равно{ "duration": { "gte": 2 } }
containsСодержит подстроку{ "name": { "contains": "клиент" } }
startsWithНачинается с{ "name": { "startsWith": "Звонок" } }
endsWithЗаканчивается на{ "name": { "endsWith": "2024" } }

Логические операторы:

{
  "where": {
    "AND": [{ "duration": { "gte": 2 } }, { "duration": { "lte": 10 } }]
  }
}
{
  "where": {
    "OR": [{ "source": "SIPUNI" }, { "source": "ORIGINAL" }]
  }
}

orderBy — сортировка

Сортировка по одному или нескольким полям:

{
  "orderBy": { "createdAt": "desc" }
}
{
  "orderBy": [{ "createdAt": "desc" }, { "name": "asc" }]
}

Значения: "asc" (по возрастанию) или "desc" (по убыванию).


take и skip — пагинация

ПараметрОписание
takeКоличество записей для возврата
skipКоличество записей для пропуска

Пример пагинации (страница 3 по 20 записей):

{
  "take": 20,
  "skip": 40
}

include — связанные данные

Включение связанных сущностей в ответ:

{
  "include": {
    "messages": true,
    "Report": true,
    "manager": true
  }
}

Можно также фильтровать включаемые данные:

{
  "include": {
    "Report": {
      "take": 1,
      "orderBy": { "createdAt": "desc" }
    }
  }
}

select — выбор полей

Вместо include можно использовать select для выбора конкретных полей:

{
  "select": {
    "id": true,
    "name": true,
    "createdAt": true,
    "Report": {
      "select": {
        "temperature": true,
        "summary": true
      }
    }
  }
}
Важно: Нельзя использовать include и select одновременно на одном уровне.

Endpoints

read:conversations

Список разговоров

POST /api/orpc/conversation/getConversations

Получение списка разговоров пользователя.

Поля модели Conversation:

ПолеТипОписание
idnumberУникальный ID разговора
namestringНазвание/заголовок разговора
durationnumberДлительность в минутах
createdAtdatetimeДата создания
sourceenumИсточник: SIPUNI, ORIGINAL
managerIdnumber?ID менеджера (если привязан)
recordingUrlstring?URL записи (если есть)

Связанные данные (include):

ПолеОписание
messagesСписок сообщений (реплик) разговора
ReportОтчёты анализа
managerДанные менеджера
MarketingInsightМаркетинговые инсайты

Примеры запросов:

Получить последние 10 разговоров:

{
  "take": 10,
  "orderBy": { "createdAt": "desc" }
}

Разговоры за январь 2024 длительностью более 2 минут:

{
  "where": {
    "createdAt": {
      "gte": "2024-01-01T00:00:00Z",
      "lte": "2024-01-31T23:59:59Z"
    },
    "duration": { "gte": 2 }
  },
  "orderBy": { "createdAt": "desc" }
}

Разговоры конкретного менеджера с отчётами:

{
  "where": { "managerId": 5 },
  "include": {
    "Report": {
      "select": {
        "id": true,
        "temperature": true,
        "summary": true
      }
    },
    "manager": true
  }
}

Поиск по названию:

{
  "where": {
    "name": { "contains": "продажа" }
  }
}

Пример ответа:

{
  "result": [
    {
      "id": 1,
      "name": "Звонок с клиентом",
      "duration": 5.2,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "source": "SIPUNI",
      "managerId": 1,
      "Report": [
        {
          "id": 1,
          "temperature": "HOT",
          "summary": "Клиент заинтересован в покупке"
        }
      ],
      "manager": {
        "id": 1,
        "name": "Иван Петров"
      }
    }
  ]
}

Детали разговора

POST /api/orpc/conversation/getConversation

Получение одного разговора по ID с полной информацией.

Обязательные параметры:

ПараметрТипОписание
where.idnumberID разговора

Поля модели Message (при include):

ПолеТипОписание
idnumberID сообщения
contentstringТекст реплики
namestringИмя говорящего
speaker_roleenumРоль: MANAGER, CLIENT
createdAtdatetimeВремя создания

Поля модели Report (при include):

ПолеТипОписание
idnumberID отчёта
temperatureenumТемпература: HOT, WARM, COLD
summarystringКраткое резюме разговора
createdAtdatetimeДата создания отчёта

Примеры запросов:

Разговор с сообщениями:

{
  "where": { "id": 123 },
  "include": { "messages": true }
}

Разговор с отчётом и результатами по критериям:

{
  "where": { "id": 123 },
  "include": {
    "messages": true,
    "Report": {
      "include": {
        "Result_Criterias": {
          "include": {
            "criteria": true
          }
        }
      }
    }
  }
}

Пример ответа:

{
  "result": {
    "id": 123,
    "name": "Звонок с клиентом",
    "duration": 5.2,
    "createdAt": "2024-01-15T10:30:00.000Z",
    "source": "SIPUNI",
    "messages": [
      {
        "id": 1,
        "content": "Здравствуйте, компания CallMeAI!",
        "name": "Менеджер",
        "speaker_role": "MANAGER"
      },
      {
        "id": 2,
        "content": "Добрый день! Меня интересует ваш продукт.",
        "name": "Клиент",
        "speaker_role": "CLIENT"
      }
    ],
    "Report": [
      {
        "id": 1,
        "temperature": "HOT",
        "summary": "Клиент заинтересован в покупке, готов к следующему шагу",
        "Result_Criterias": [
          {
            "id": 1,
            "value": 95,
            "comment": "Отличное приветствие",
            "criteria": {
              "id": 1,
              "name": "Приветствие"
            }
          }
        ]
      }
    ]
  }
}

analyze:calls

Загрузка и анализ звонка

POST /api/orpc/uploadCall

Загрузка аудиофайла для анализа. Возвращает поток событий (Server-Sent Events).

Content-Type: multipart/form-data

Обязательные параметры:

ПараметрТипОписание
fileFileАудиофайл для анализа
criteriaFolderIdnumberID папки критериев для оценки

Опциональные параметры:

ПараметрТипОписание
managerIdnumberID менеджера для привязки звонка

Поддерживаемые форматы файлов:

ФорматMIME-тип
MP3audio/mpeg
WAVaudio/wav
M4Aaudio/mp4
OGGaudio/ogg
WEBMaudio/webm

Ограничения:

  • Максимальный размер файла: 100 МБ
  • Максимальная длительность: 60 минут
  • Минимальная длительность: 30 секунд
  • Требуется наличие минут на балансе
Списание минут: 1 минута аудио = 1 минута с баланса. Округление вверх (5:30 → 6 минут).

Пример запроса (curl):

curl -X POST https://callmeai.ru/api/orpc/uploadCall \
  -H "Authorization: Bearer mai_xxxxx" \
  -F "file=@call.mp3" \
  -F "criteriaFolderId=1" \
  -F "managerId=5"

Пример запроса (JavaScript):

const formData = new FormData();
formData.append("file", audioFile);
formData.append("criteriaFolderId", "1");
formData.append("managerId", "5");

const response = await fetch("https://callmeai.ru/api/orpc/uploadCall", {
  method: "POST",
  headers: {
    Authorization: "Bearer mai_xxxxx",
  },
  body: formData,
});

Поток ответа (SSE):

Endpoint возвращает поток событий с прогрессом обработки:

data: {"type": "chunk", "choices": [{"delta": {"content": "..."}}]}
data: {"type": "chunk", "choices": [{"delta": {"content": "..."}}]}
...
data: {"type": "conversation", "data": {"id": 456, "name": "Новый звонок", ...}}

Финальный объект conversation содержит:

ПолеТипОписание
idnumberID созданного разговора
namestringАвтоматически сгенерированное название
durationnumberДлительность в минутах
createdAtdatetimeДата создания
messagesarrayТранскрибированные реплики

Обработка SSE в JavaScript:

const response = await fetch(url, options);
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value);
  const lines = text.split("\n");

  for (const line of lines) {
    if (line.startsWith("data: ")) {
      const data = JSON.parse(line.slice(6));

      if (data.type === "conversation") {
        console.log("Готово!", data.data);
      }
    }
  }
}

Возможные ошибки:

ОшибкаПричина
Недостаточно минутНа балансе меньше минут, чем длительность аудио
Файл слишком большойПревышен лимит 100 МБ
Неподдерживаемый форматФайл не является аудио
Папка критериев не найденаНеверный criteriaFolderId

read:criteria

Список критериев

POST /api/orpc/criteria/getCriterias

Получение списка критериев оценки.

Поля модели Criteria:

ПолеТипОписание
idnumberУникальный ID критерия
namestringНазвание критерия
descriptionstringОписание — что именно оценивается
criteriaFolderIdnumberID папки критериев
createdAtdatetimeДата создания
updatedAtdatetimeДата последнего обновления

Связанные данные (include):

ПолеОписание
CriteriaFolderПапка, к которой принадлежит критерий

Фильтрация (where):

ПолеОписание
idПо ID критерия
nameПо названию (contains, startsWith и т.д.)
criteriaFolderIdПо ID папки
createdAtПо дате создания

Примеры запросов:

Все критерии из папки:

{
  "where": { "criteriaFolderId": 1 }
}

Поиск критериев по названию:

{
  "where": {
    "name": { "contains": "приветствие" }
  }
}

Критерии с информацией о папке:

{
  "include": { "CriteriaFolder": true }
}

Пример ответа:

{
  "result": [
    {
      "id": 1,
      "name": "Приветствие",
      "description": "Менеджер представился и поздоровался",
      "criteriaFolderId": 1,
      "createdAt": "2024-01-10T08:00:00.000Z",
      "updatedAt": "2024-01-10T08:00:00.000Z"
    },
    {
      "id": 2,
      "name": "Выявление потребностей",
      "description": "Менеджер задал вопросы о потребностях клиента",
      "criteriaFolderId": 1,
      "createdAt": "2024-01-10T08:00:00.000Z",
      "updatedAt": "2024-01-10T08:00:00.000Z"
    }
  ]
}

Детали критерия

POST /api/orpc/criteria/getCriteria

Получение одного критерия по ID.

Обязательные параметры:

ПараметрТипОписание
where.idnumberID критерия

Пример запроса:

{
  "where": { "id": 1 }
}

Пример ответа:

{
  "result": {
    "id": 1,
    "name": "Приветствие",
    "description": "Менеджер представился и поздоровался",
    "criteriaFolderId": 1,
    "createdAt": "2024-01-10T08:00:00.000Z",
    "updatedAt": "2024-01-10T08:00:00.000Z"
  }
}

write:criteria

Создание критерия

POST /api/orpc/criteria/createCriteria

Создание нового критерия оценки.

Обязательные поля в data:

ПолеТипОписание
namestringНазвание критерия (макс. 255 символов)
descriptionstringОписание — что именно оценивается
CriteriaFolder.connect.idnumberID папки для привязки критерия
Как получить ID папки: Используй endpoint getCriterias — в ответе каждый критерий содержит criteriaFolderId.

Пример запроса:

{
  "data": {
    "name": "Закрытие сделки",
    "description": "Менеджер предложил оформить заказ и назвал следующий шаг",
    "CriteriaFolder": {
      "connect": { "id": 1 }
    }
  }
}

Пример ответа:

{
  "result": {
    "id": 15,
    "name": "Закрытие сделки",
    "description": "Менеджер предложил оформить заказ и назвал следующий шаг",
    "criteriaFolderId": 1,
    "createdAt": "2024-01-20T14:30:00.000Z",
    "updatedAt": "2024-01-20T14:30:00.000Z"
  }
}

Обновление критерия

POST /api/orpc/criteria/updateCriteria

Обновление существующего критерия. Можно обновить только название, только описание или оба поля.

Обязательные параметры:

ПараметрТипОписание
where.idnumberID критерия для обновления

Опциональные поля в data:

ПолеТипОписание
namestringНовое название
descriptionstringНовое описание

Примеры запросов:

Обновить только название:

{
  "where": { "id": 1 },
  "data": {
    "name": "Приветствие клиента"
  }
}

Обновить название и описание:

{
  "where": { "id": 1 },
  "data": {
    "name": "Приветствие клиента",
    "description": "Менеджер представился, назвал компанию и поприветствовал клиента"
  }
}

Пример ответа:

{
  "result": {
    "id": 1,
    "name": "Приветствие клиента",
    "description": "Менеджер представился, назвал компанию и поприветствовал клиента",
    "criteriaFolderId": 1,
    "createdAt": "2024-01-10T08:00:00.000Z",
    "updatedAt": "2024-01-20T15:00:00.000Z"
  }
}

Удаление критерия

POST /api/orpc/criteria/deleteCriteria

Удаление критерия. Операция необратима.

Обязательные параметры:

ПараметрТипОписание
where.idnumberID критерия для удаления
Внимание: При удалении критерия удаляются все связанные результаты оценки (ResultCriteria) во всех отчётах.

Пример запроса:

{
  "where": { "id": 15 }
}

Пример ответа:

{
  "result": {
    "id": 15,
    "name": "Закрытие сделки",
    "description": "Менеджер предложил оформить заказ и назвал следующий шаг",
    "criteriaFolderId": 1,
    "createdAt": "2024-01-20T14:30:00.000Z",
    "updatedAt": "2024-01-20T14:30:00.000Z"
  }
}

Коды ошибок

КодHTTPОписание
UNAUTHORIZED401Токен отсутствует, неверный или просрочен
FORBIDDEN403Токен не имеет необходимого скоупа
NOT_FOUND404Запрашиваемый ресурс не найден
BAD_REQUEST400Неверный формат запроса
INTERNAL_SERVER_ERROR500Внутренняя ошибка сервера

Примеры ошибок:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "API token has expired"
  }
}
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Missing required scope. Need one of: read:conversations"
  }
}

Примеры на разных языках

JavaScript (fetch)

const response = await fetch(
  "https://callmeai.ru/api/orpc/conversation/getConversations",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer mai_xxxxx",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      take: 10,
      orderBy: { createdAt: "desc" },
    }),
  },
);

const data = await response.json();
console.log(data.result);

Python (requests)

import requests

response = requests.post(
    'https://callmeai.ru/api/orpc/conversation/getConversations',
    headers={
        'Authorization': 'Bearer mai_xxxxx',
        'Content-Type': 'application/json'
    },
    json={
        'take': 10,
        'orderBy': {'createdAt': 'desc'}
    }
)

data = response.json()
print(data['result'])

Python (загрузка файла)

import requests

with open('call.mp3', 'rb') as f:
    response = requests.post(
        'https://callmeai.ru/api/orpc/uploadCall',
        headers={
            'Authorization': 'Bearer mai_xxxxx'
        },
        files={'file': f},
        data={
            'criteriaFolderId': 1,
            'managerId': 5
        }
    )

Rate Limiting

В данный момент лимиты на количество запросов не установлены. При большой нагрузке рекомендуется:

  • Делать паузы между запросами (100-500мс)
  • Использовать пагинацию для больших объёмов данных
  • Кешировать данные на стороне клиента

Часто задаваемые вопросы

Как получить criteriaFolderId?

Используй endpoint getCriterias без фильтра — в ответе будет criteriaFolderId для каждого критерия.

Можно ли получить все разговоры за период?

Да, используй параметр where:

{
  "where": {
    "createdAt": {
      "gte": "2024-01-01T00:00:00Z",
      "lte": "2024-01-31T23:59:59Z"
    }
  }
}

Как обрабатывать SSE для uploadCall?

Endpoint возвращает поток событий. В JavaScript используй ReadableStream:

const response = await fetch(url, options);
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const chunk = decoder.decode(value);
  console.log(chunk);
}

Что дальше?