API и вебхуки для вашей CRM

REST API отдаёт и принимает лиды, звонки, разборы и диалоги. Вебхуки сообщают о событиях, как только они происходят. Ключи и адреса вебхуков выдаёт команда Phonix.

terminal
# Новые лиды
curl "https://app.phonix.website/api/v1/leads?status=new&limit=50" \
  -H "Authorization: Bearer $PHONIX_KEY"

# Отправить звонок менеджера на разбор
curl -X POST https://app.phonix.website/api/v1/calls \
  -H "Authorization: Bearer $PHONIX_KEY" \
  -F file=@call.mp3 -F client_number=79261234567 \
  -F employee_extension=101 -F external_id=crm-call-8812

Основное

Базовый адрес
https://app.phonix.website/api/v1
Авторизация
Authorization: Bearer phx_…
Права ключа
чтение и запись или только чтение
Лимит
600 запросов в минуту на ключ
Формат
JSON, даты в ISO 8601, списки страницами
Спецификация
OpenAPI 3.1, GET /api/v1/openapi.json

Ресурсы

АдресЧто можно
/leadsсписок с поиском и фильтрами, создание, правка, хронология, заметки
/callsсписок, карточка с расшифровкой и оценкой, загрузка файла или ссылки, повторный разбор, скачивание записи
/qa-scripts, /employeesскрипты-чек-листы и сотрудники для речевой аналитики
/analytics/overview, /analytics/qaсводка за период и выполнение скрипта по пунктам и сотрудникам
/voice-agents, /bots, /bots/{id}/knowledgeнастройка агентов, ботов и базы знаний
/channelsканалы компании и код виджета
/conversationsдиалоги всех каналов, ответ оператора, передача боту
/webhooks/event-typesкакие события Phonix присылает на вебхуки

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

const res = await fetch('https://app.phonix.website/api/v1/calls?passed=false&limit=20', {
  headers: { Authorization: `Bearer ${process.env.PHONIX_KEY}` },
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { data } = await res.json();
for (const call of data) console.log(call.employee_name, call.score, call.summary);

Вебхуки

Phonix отправляет POST на адрес вашей CRM. Тело: {"id", "type", "created_at", "data"}, где data содержит объект в том же формате, что и API.

СобытиеКогда приходит
lead.createdНовый лид: от агента, бота, звонка менеджера, API или вручную
lead.updatedИзменились статус, поля или ответственный
call.completedЗвонок завершён, запись сохранена
call.analyzedРазбор готов: балл, вердикты по пунктам, итог
conversation.createdКлиент начал новый диалог в одном из каналов
conversation.messageНовое сообщение клиента, бота или оператора
conversation.handoffДиалог передан оператору
POST https://crm.example/hooks/phonix
{
  "id": "6f0c3a52-8d1e-4b07-9a51-2c4f5e7d9b10",
  "type": "lead.created",
  "created_at": "2026-10-01T08:18:44Z",
  "data": {
    "id": "a1d9e0f4-3c2b-4f6a-8e7d-5b4c3a2f1e0d",
    "object": "lead",
    "name": "Михаил",
    "phone": "79161234512",
    "status": "new",
    "source": "voice",
    "fields": {
      "request": "Кухня на заказ, угловая 3,2 × 2 м",
      "address": "Ленинский проспект, 42",
      "visit": "Суббота, с 10:00 до 12:00"
    }
  }
}

Заголовки запроса

Phonix-Event
тип события, например call.analyzed
Phonix-Delivery
id доставки: по нему удобно отбрасывать повторы
Phonix-Signature
t=<unix>,v1=<hex>, где v1 = HMAC-SHA256(secret, "<t>.<тело>")

Проверка подписи

Считайте HMAC от сырого тела запроса до разбора JSON и сравнивайте за постоянное время. Метку времени старше 5 минут лучше отклонять.

import crypto from 'node:crypto';

// rawBody: тело запроса строкой, до JSON.parse
export function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
  const given = String(parts.v1 || '');
  return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

Повторы при ошибке

Если ваш сервер ответил не 2xx или не ответил за 10 секунд, Phonix повторит отправку. После 50 неудач подряд адрес отключается, причина видна в панели.

  1. #1 сразу
  2. #2 1 мин
  3. #3 5 мин
  4. #4 30 мин
  5. #5 2 ч
  6. #6 6 ч
  7. #7 12 ч

Ошибки

Ошибка всегда приходит в одном формате: машинный code, сообщение для разработчика и подробности, если они есть.

HTTP 400
{
  "error": {
    "code": "bad_request",
    "message": "Invalid body",
    "details": [
      {
        "path": "phone",
        "message": "Invalid phone number",
        "code": "custom"
      }
    ]
  }
}
СтатусcodeКогда
400bad_requestтело или параметры не прошли проверку, в details список полей
401unauthorizedнет ключа или ключ отозван
403forbiddenу ключа нет права на запись
404not_foundобъекта нет или он принадлежит другой компании
409conflictтакой объект уже есть, например звонок с тем же external_id
429rate_limitedбольше 600 запросов в минуту на ключ

Справочник и вебхуки в панели

Справочник API в панели Phonix: методы с описаниями
Справочник API со всеми методами и примером запроса.
Настройка вебхуков компании в панели Phonix
Адрес вебхука, выбранные события и журнал доставки.