Перейти к содержимому
AISARAISAR
REST API

Команды и пользователи

Управление командами (группировка операторов + доступ к каналам), участниками компании и приглашениями новых пользователей по email. Группа также включает блок текущего пользователя (GET /me): профиль с ролями и правами в активной компании, статус доступности и переключение активной компании. GET /v1/users перечисляет реальных (не сервисных) пользователей компании. Отдельный блок — провижининг SIP-телефонии операторам (companies/current/members/telephony*, админский гейт company_telephony.manage) и самообслуживание оператора (/v1/softphone/*, только сессия/мобильный токен). GET /v1/bootstrap собирает стартовый пакет кабинета (профиль, подписку, конфиги, непрочитанные) одним запросом вместо девяти.

Роли и членство

Роль назначается на уровне компании (admin/manager/agent) через Spatie permissions с teams-режимом — один пользователь может состоять в нескольких компаниях с разными ролями.

POST /v1/companies/current/members/{memberId}/role и .../revoke работают только с людьми: сервисный аккаунт API-токена (см. группу «API-токены») этими эндпоинтами не понижается и не отзывается — для него ответ 404 с тем же телом "User not found.", что и для несуществующего пользователя. Отключить интеграцию можно только удалением её API-токена (DELETE /v1/api-tokens/{id}). Смена роли отправляет вебхук user.role_changed с source: "company" — только если роль действительно изменилась: повторное назначение той же роли события не создаёт.

Команды

Команда группирует операторов и открывает им доступ к набору каналов (см. «Права доступа к каналу» в группе «Каналы»). Когда для платформы включены командные ограничения доступа к каналам (см. там же), менять СОСТАВ команды (member_ids) и её КАНАЛЫ (channel_access_mode/channel_ids) на POST/PATCH /v1/teams может только администратор компании или сотрудник платформы — иначе 403 {"message": "...", "code": "channel_access_admin_only"}, и запрос откатывается целиком. Решение принимается по фактическому ЭФФЕКТУ запроса, а не по тому, какие ключи в нём присутствуют: переименование команды (name/description) без изменения состава или каналов проходит у любого пользователя с правом team.update, даже если тело запроса заново отправляет прежние channel_ids — форма кабинета обычно отправляет их всегда, и это не расценивается как попытка их сменить, пока итоговый список не отличается от текущего.

Сохранение команды (channel_ids) принимает как живые, так и архивные каналы компании — правило команды на архивном канале не теряется при обычном сохранении (например при переименовании, если форма пересылает список каналов целиком), даже несмотря на то, что доступ к архивному каналу в интерфейсе обычно не редактируется напрямую. Отдельно от этого, канал, ожидающий окончательного удаления (DELETE /channels/archived/{channelId} уже поставил его в очередь), НЕ принимается как новое значение channel_ids и не удаляется из уже сохранённого списка правил команды — он просто исключён из проверки: такое правило исчезнет само, вместе с каналом, когда очередь на удаление завершится.

DELETE /v1/teams/{team} тоже подчиняется тому же правилу: удаление команды снимает её правила доступа к каналам, поэтому оно требует администратора/сотрудника платформы, только если у команды действительно есть хотя бы одно правило доступа (channel_access) — команду без единого правила может удалить любой держатель права team.delete, при включённых ограничениях в том числе. Отдельно от прав на команды: бывший сотрудник автоматически исключается из ВСЕХ команд компании в момент, когда он теряет в ней роль (увольнение, отзыв приглашения, выход) — держать список членов команды в актуальном состоянии вручную не требуется.

Приглашения

Приглашение живёт 30 дней (expires_at). Статус приглашения принимает значения pending, accepted, declined, expired и revoked, и наружу он всегда вычисляется с учётом срока: приглашение с прошедшим expires_at приходит как expired, а не как pending. Перевод записи в конечный статус выполняет фоновая уборка (каждые 15 минут), но ответ на неё не рассчитывает — просроченное приглашение отдаётся как expired и до её прохода. Поэтому сравнивать expires_at с текущим временем на своей стороне не нужно: pending в ответе означает, что приглашение действительно ещё можно принять.

Тот же признак приходит в поле invitation_status каждой строки GET /v1/companies/current/members: accepted — у уже присоединившегося участника, у строки-приглашения — её статус с учётом срока.

Если в POST /v1/invitations указан email уже действующего участника компании, запрос отклоняется с 422 и errors.email, error: "member_already_exists" — приглашение не создаётся и место в лимите пользователей не расходуется.

Повторное приглашение того же email, пока предыдущее ещё активно (pending, срок не истёк), не создаёт вторую запись: обновляется та же запись — новый guid (прежняя ссылка перестаёт работать), новая роль и новый срок действия. Проверка лимита пользователей в этом случае не выполняется — обновление действующего приглашения места не расходует.

Гонка из двух одновременных запросов на приглашение одного email в одной компании не создаёт дубль и не отдаёт 500: проигравший запрос получает тот же результат, что и повторное приглашение выше — то же приглашение с новым guid, запрошенными ролью и сроком действия.

Публичные шаги разрешения приглашения (resolve/context/intent, без auth) в этот справочник не включены — см. руководство по приглашениям.

Текущий пользователь

Блок текущего пользователя (GET /me) отдаёт профиль вместе с ролями и правами в активной компании — это отправная точка для потребителей сервисных токенов; сопутствующие эндпоинты обновляют профиль, статус доступности и активную компанию (при переключении компании профиль возвращает роли и права уже новой компании).

С 2026-09-26 channel_ids в GET /me — только каналы компании (кроме черновиков), доступные пользователю по правилам channel_access; администратор по-прежнему получает все.

С 2026-09-30 под embed-токеном (привязан к одной компании ability company:{id} — см. группу «API-токены» и партнёрскую интеграционную документацию) GET /me сужен до этой компании: memberships содержит ровно одну запись — привязанную компанию, pending_invitations всегда [] (их guid — предъявительский секрет на вступление в ДРУГУЮ компанию), has_partner_access всегда false. Раскрывать партнёрскому origin, в каких ещё компаниях состоит оператор, нет причины — токен и так не может работать нигде, кроме привязанной компании. Для сессии кабинета, мобильного токена и обычных (*-ability) сервисных токенов ответ не меняется.

С 30.09.2026 GET /me (и профиль в ответе PUT /v1/me/current-company — та же форма) несёт булево поле can_edit_channel_access: может ли ТЕКУЩИЙ пользователь прямо сейчас менять правила доступа к каналам (.../access) и состав/список каналов команд в активной компании (см. «Права доступа к каналу» в группе «Каналы» и «Команды» ниже). Пока командные ограничения доступа выключены для платформы (текущее поведение по умолчанию), поле всегда true; когда включены — true только у администратора компании и сотрудника платформы, у остальных — false. Само поле ничего не резервирует и не гарантирует: обычные права (channel.update для доступа к каналу, team.* для команд) по-прежнему нужны и проверяются отдельно — кабинет использует can_edit_channel_access вместе с ними, чтобы решить, показывать ли элементы управления, а не вместо них.

Статус доступности (PUT /v1/me/availability) принимает ровно три значения на запись: online, away, offline (busy — системное состояние, через этот эндпоинт не выставляется).

С 2026-09-23 photo в POST /v1/me больше не свободная строка: значение должно быть либо абсолютной http(s)-ссылкой, либо путём вида {current_company_id}/users/... — любое другое значение отклоняется с 422. Подписанную ссылку на фото, которую сама же выдала в GET /v1/me, можно прислать обратно без изменений — это читается как «фото не менялось», и такое значение просто не сохраняется заново.

Активная компания

Активная компания хранится в едином серверном поле current_company_id вашего аккаунта: оно общее для всех ваших сессий и API-токенов (не привязано к отдельному токену), и по нему скоупятся company-ориентированные списки (например, GET /v1/conversations) и авторизация realtime-каналов.

Поэтому если тот же пользователь переключит компанию в другой вкладке, приложении или токене, за ним последуют все контексты — встроенный чат может внезапно показать диалоги другой компании, а подписка на каналы прежней (уже не активной) компании начнёт отвечать 403. Держите компанию, из которой читаете и на каналы которой подписаны, синхронной с current_company_id.

Настройки уведомлений

GET /v1/me/notification-preferences отдаёт снимок из четырёх блоков: channels — булевы мастер-переключатели по каналу доставки (mail, push, whatsapp; отсутствующий ключ трактуется как true), categories — объект по каждой категории уведомлений с полями enabled/label/controllable, а также push_triggers и quiet_hours (см. ниже). Категория transactional (коды подтверждения, приглашения, сброс пароля) всегда controllable: false и enabled: true — её нельзя отключить; остальные категории (onboarding, trial, billing, win_back, engagement) пользователь может выключать по отдельности.

PUT /v1/me/notification-preferences — частичное обновление (merge, не replace): передавайте только те ключи channels.*/categories.*/push_triggers.*/quiet_hours.*, которые меняете — остальные ключи, включая другие блоки целиком, сохраняют текущее значение. channels.whatsapp = false — единственный способ для пользователя отказаться от WhatsApp-канала биллинговых уведомлений (напоминания об оплате, приостановке и т.п.) — этот отказ обязателен политикой Meta для WhatsApp-шаблонов и должен уважаться отправителем.

Оба эндпоинта также несут push_triggers — объект булевых переключателей по типу push-уведомления: chat_message_new, conversation_assigned, call_missed, call_incoming, company_unassigned. По умолчанию включены все, кроме company_unassigned (пуш о том, что диалог в компании остался без ответственного — опционален, чтобы не заваливать им сразу всех операторов). call_incoming фактически всегда доставляется независимо от сохранённого значения — входящий звонок это системное уведомление, которое не подавляется.

quiet_hours — окно «не беспокоить»: { "enabled": bool, "start": "HH:MM"|null, "end": "HH:MM"|null }, по умолчанию enabled: false и start/end — null. Пока окно активно, некритичные push-уведомления (например, conversation_assigned) подавляются — кроме call_incoming, который тихие часы не затрагивают. Время трактуется в часовом поясе пользователя (users.timezone); если start больше end, окно считается ночным (переходит через полночь, например 22:00–08:00).

Подтверждение номера телефона

Номер из профиля (users.phone) можно подтвердить кодом, который приходит в WhatsApp: POST /v1/me/phone/send-otp отправляет 6-значный код, POST /v1/me/phone/verify с телом {"code": "123456"} его проверяет. Подтверждённый номер отражается полем phone_verified в GET /v1/me и служит адресом для WhatsApp-дубликатов биллинговых уведомлений. Изменение номера через POST /v1/me сбрасывает подтверждение.

send-otp не принимает номер в теле запроса — код всегда уходит на сохранённый номер профиля, поэтому сначала сохраните номер, а затем запрашивайте код. Ответ 200 содержит маскированный phone и expires_in (секунды жизни кода, сейчас 600). Возможные ошибки: 422 — номер не задан либо не проходит проверку формата E.164; 429 — превышены лимиты отправки/проверки; 409 (только у verify) — номер изменился между отправкой и проверкой, запросите код заново; 502 — сообщение не удалось доставить; 503 — исчерпан суточный лимит отправки кодов на платформе.

Оба эндпоинта доступны только сессионной авторизации и мобильным токенам. Сервисные аккаунты, клиентские API-токены и токены встраивания получают 403 — подтверждение номера остаётся действием живого пользователя.

Телефония операторов

Провижининг SIP-телефонии оператору — шаг, следующий за подключением SIP-канала (см. руководство по подключению каналов, раздел «SIP-телефония (BYO-транк)»). Администратор компании (право company_telephony.manage) включает телефонию конкретному участнику через POST /v1/companies/current/members/{memberId}/telephony/enable, что провижинит СРАЗУ два SIP-профиля: браузерный agent-{id}-web (для встроенного WebRTC-софтфона в приложениях AISAR, пароль непрозрачный) и внешний agent-{id} (для настольных SIP-телефонов и приложений вроде Zoiper/MicroSIP/Linphone, пароль типизируемый). Отдельного тумблера для внешнего профиля нет — disable снимает оба профиля разом.

Административные эндпоинты (companies/current/members/telephony*) вызываются company api_token наравне с остальной группой — партнёрский сервис-аккаунт с ролью admin (которая безусловно несёт company_telephony.manage, см. ниже) может провижинить операторов программно. Самообслуживание (/v1/softphone/*) — обратная ситуация: эти эндпоинты требуют, чтобы вызывающий БЫЛ тем самым оператором (сессионная авторизация или мобильный токен — тот же паттерн, что у подтверждения номера телефона выше), поэтому company api_token / сервисный аккаунт получает здесь 403 — своего SIP-профиля у сервисного аккаунта никогда нет (провижининг участников сознательно фильтрует is_service_account = false). Партнёрам доступен также server-to-server способ включения телефонии операторам без пользовательской сессии — он описан в партнёрской интеграционной документации.

company_telephony.manage не тарифный гейт: с 13.08.2026 роль admin в ЛЮБОЙ компании безусловно несёт это право (бэкфилл-миграция закрыла разрыв для компаний, заведённых раньше). Тарифный гейт живёт отдельно — фича telephony плана нужна оператору, чтобы САМОМУ пользоваться звонками (TelephonyAccess::availableTo), но не нужна администратору, чтобы управлять операторами, схемами и каналами.

Обе точки получения кредов внешнего профиля — админская GET .../telephony/credentials и операторская GET /v1/softphone/external-credentials — используют один и тот же построитель (ExternalSipCredentialPresenter), поэтому форма ответа не может разойтись: sip_username, sip_password, host, tcp_port (5060), tls_port (5061), default_transport: "TCP", codecs: ["opus", "PCMU", "PCMA"], media_encryption, registered (жива ли SIP-регистрация прямо сейчас — best-effort проверка через ARA, при ошибке трактуется как false) и provisioning_uri — generic sip:-URI (sip:user:pass@host:port;transport=tcp) для импорта по QR в Zoiper / MicroSIP / Linphone / Grandstream Wave. Не подходит для 3CX — его приложение читает только собственный формат провижининг-QR, там поля вводятся вручную. Браузерный (agent-{id}-web) пароль непрозрачный: GET .../telephony/credentials (админ) его вообще не возвращает — эндпоинт трогает только внешний профиль; а вот POST .../telephony/reset-password (админ), наоборот, ЕГО возвращает — ответ несёт свежую пару sip_username/sip_password браузерного профиля (тот же принцип одноразового показа, что у создания API-токена), и заодно молча ротирует внешний профиль, не отдавая его новый пароль в этом же ответе — свой новый внешний пароль оператор увидит через собственный /softphone/external-credentials.

GET /v1/companies/current/members/telephony отдаёт по строке на каждого реального (не сервисного) участника компании: user_id, name, email, photo, telephony_enabled (активен ли браузерный профиль), browser_registered, extension, external_enabled (активен ли внешний профиль) и external_registered. Пара *_registered — это живая SIP-регистрация прямо сейчас, отдельно для браузерного софтфона (browser_registered) и для настольного телефона / внешнего SIP-приложения (external_registered); оба флага снимаются одним batched-запросом к ARA, и при недоступности ARA оба трактуются как false — список операторов из-за этого не падает. Это булев признак «сейчас или нет»: история регистраций не хранится, поэтому «не подключён с 09:40» из ответа не выводится. Профиль встраиваемого софтфона (embed) в этих флагах не учитывается — это софтфон в iframe партнёрской панели, а не устройство оператора компании.

С 2026-10-20 каждая строка GET /v1/companies/current/members/telephony несёт блок outbound — с чего сотрудник звонит: { "schemes": [ { "id", "name", "via", "channel_ids" } ], "primary_scheme_id": string|null, "primary_source": string|null, "falls_back_to": string|null }. schemes — исходящие схемы, в которых сотрудник состоит (via — лично или через команду; channel_ids — линии схемы); primary_scheme_id и primary_source — какая схема основная и как это определено: explicit (выбрана админом), implicit (выбрана автоматически по прежнему порядку: личная раньше командной) или single (единственная схема); null, если схем нет; falls_back_to заполнен только у сотрудника без схем: company_default (общий номер), legacy (любая доступная линия) или company_default_unavailable (общий номер задан, но недоступен — звонки сотрудника уходят в отказ). Эндпоинты .../telephony* этой группы закрыты для embed-токенов: список теперь называет схемы.

PUT /v1/companies/current/members/{memberId}/telephony/outbound-primary делает одну из схем сотрудника основной: тело { "scheme_id": "uuid" | null } (поле обязательно; null снимает явный выбор и возвращает автоматический). Схема должна быть исходящей, активной, своей компании, а сотрудник — уже в ней состоять; эндпоинт никого в схему не добавляет. Ответ — строка сотрудника в том же виде, что в списке выше (с обновлённым блоком outbound). Отказы 422: scheme_not_found (чужая, черновая или несуществующая схема — без различий) и not_a_member_of_scheme. POST /v1/companies/current/members/telephony/outbound-primary/bulk делает одну схему основной сразу многим: тело { "user_ids": [int, ...], "scheme_id": "uuid" } (от 1 до 500 id; поле target необязательно и допускает только scheme; отдельного массового действия «свой прямой номер» нет). Ответ: { "data": { "updated": [user_id, ...], "skipped": [ { "user_id", "reason": "not_in_scheme" } ] } }. Один id постороннего человека отклоняет весь запрос целиком (422), ничего не записывается. Оба эндпоинта требуют права company_telephony.manage и полноправного токена, недоступны embed-токенам, ограничены лимитером telephony-outbound-primary и пишут аудит (telephony.outbound_primary.set / .bulk_set) и WebSocket-событие telephony.outbound.updated.

POST .../telephony/extension — внутренний номер оператора, 3–4 цифры (^\d{3,4}$), уникален в пределах компании; пустое значение снимает номер. Требует, чтобы телефония уже была включена — иначе 422.

Самообслуживание оператора (только сессия/мобильный токен): GET /v1/softphone/credentials — собственные браузерные креды (используются встроенным WebRTC-софтфоном напрямую, не для ручного ввода); POST /v1/softphone/reset-password — их ротация; GET /v1/softphone/external-credentials / POST /v1/softphone/external/reset-password — то же для внешнего профиля, включая provisioning_uri для QR; GET /v1/softphone/colleagues — телефонные коллеги по компании (директория набора) с extension и статусом доступности; POST /v1/softphone/internal-call — звонок коллеге по callee_user_id либо внутреннему номеру; POST /v1/softphone/external-call — исходящий звонок через SIP-транк компании на внешний номер, с опциональным contact_id (резолвится строго в рамках компании — чужой/несуществующий id это 422 без молчаливого фолбэка на резолв по номеру). external-call разделяет с POST /v1/calls/originate общий лимит call-originate — 20 запросов в минуту на пользователя; те же ограничения по sip_outbound_gated и по антизлоупотребительной проверке IP-режима, что и у /calls/originate, применяются и здесь (см. группу «Звонки»).

Bootstrap оболочки кабинета

GET /v1/bootstrap — один запрос вместо девяти отдельных, которые кабинет (my.aisar.app) делает при открытии. Части: me (GET /v1/me), subscription (GET /v1/subscription), usage (GET /v1/subscription/usage), plans (GET /v1/plans), subscription_config (GET /v1/subscription/config), bank_transfer_config (GET /v1/billing/bank-transfer/config), nudges (GET /v1/nudges), ai_pending_actions (GET /v1/ai-pending-actions), chat_behavior (GET /v1/companies/current/chat-behavior), notifications_unread_count (GET /v1/notifications/unread-count) и tasks_counters (GET /v1/deal-tasks/counters, с 2026-09-26; у компании без фичи сделок — null и статус отказа). По умолчанию отдаются все одиннадцать частей; ?parts=a,b,c запрашивает подмножество — неизвестное имя части или повтор в списке отклоняются 422.

Каждая часть строится тем же контроллером, тем же FormRequest и той же маршрутной middleware, что и её отдельный эндпоинт, — форма и права части не описываются заново и не могут разойтись с источником (тот же гейт подписки, тот же гейт типа компании, те же права). Ответ: { "data": { "parts": {...}, "status": {...}, "errors": {...} } }. parts.<name> — тело части, развёрнутое точно так же, как это делает клиент кабинета (берётся data ответа, если оно есть, иначе тело целиком), либо null, если часть не удалась. status.<name> — HTTP-статус, который вернул бы отдельный эндпоинт. errors.<name> присутствует только для 4xx (то же JSON-тело, что дал бы отдельный эндпоинт — отказ по правам, по типу компании, по подписке и т. п.); при 5xx записи в errors нет, parts.<name> всё равно null, а инцидент логируется на сервере — в этом случае клиенту нужно обратиться к отдельному эндпоинту части напрямую. Один упавший блок не валит остальные.

Доступ — только сессия кабинета (cookie) или токен с правом * (сервисные аккаунты, включая обычный company api_token, и мобильное приложение). Токен, ограниченный allowlist'ом путей (embed:*, connect-session:*), получает 403 независимо от того, что разрешил бы allowlist каждой части по отдельности — у встроенного чата/софтфона и у визарда подключения канала нет причин запрашивать стартовый пакет кабинета целиком. Без авторизации — 401. Отдельный лимитер shell-bootstrap — 30 запросов в минуту на пользователя (сессионные вызовы по cookie не покрываются общим лимитом bearer-токенов, а один вызов строит работу десяти эндпоинтов).

Эндпоинты

МетодПуть
GET/v1/teams

Список команд

POST/v1/teams

Создание команды

GET/v1/teams/{team}

Получение команды

PATCH/v1/teams/{team}

Обновление команды

DELETE/v1/teams/{team}

Удаление команды

GET/v1/companies/current/members

Список участников текущей компании

POST/v1/companies/current/members/{memberId}/role

Смена роли участника

POST/v1/companies/current/members/{memberId}/revoke

Отзыв членства участника

GET/v1/users

Список пользователей текущей компании (без сервисных аккаунтов)

POST/v1/invitations

Отправка приглашения (email + роль); не более 6 в минуту на пользователя, сверх этого 429

POST/v1/invitations/{guid}/accept

Принятие приглашения

POST/v1/invitations/{guid}/decline

Отклонение приглашения

POST/v1/invitations/{invitationId}/role

Смена роли в приглашении

POST/v1/invitations/{invitationId}/revoke

Отзыв приглашения

DELETE/v1/invitations/{invitationId}

Удаление приглашения

GET/v1/bootstrap

Оболочка кабинета одним запросом: me, subscription, usage, plans, subscription_config, bank_transfer_config, nudges, ai_pending_actions, chat_behavior, notifications_unread_count. ?parts=a,b — подмножество; неизвестная/повторённая часть — 422. Только сессия кабинета или токен с правом *; лимит 30/мин.

GET/v1/me

Текущий профиль пользователя (роли и права)

POST/v1/me

Обновление профиля

POST/v1/me/password

Смена пароля

PUT/v1/me/availability

Установить статус доступности (online, away, offline)

PUT/v1/me/current-company

Переключение активной компании (единое поле для всех сессий/токенов)

GET/v1/me/notification-preferences

Снимок настроек уведомлений: каналы (mail/push/whatsapp) + категории + push-триггеры (push_triggers) + тихие часы (quiet_hours)

PUT/v1/me/notification-preferences

Частичное обновление настроек (channels.mail/push/whatsapp, categories.*, push_triggers.*, quiet_hours)

POST/v1/me/phone/send-otp

Отправка кода подтверждения в WhatsApp на сохранённый номер профиля

POST/v1/me/phone/verify

Проверка кода и подтверждение номера профиля

GET/v1/companies/current/members/telephony

Список реальных участников компании с телефонным статусом (включена ли, extension, зарегистрирован ли прямо сейчас браузерный софтфон и настольный телефон) — для админ-таблицы операторов.

POST/v1/companies/current/members/{memberId}/telephony/enable

Включить телефонию участнику: провижинит браузерный (agent-{id}-web) и внешний (agent-{id}) SIP-профили одновременно.

POST/v1/companies/current/members/{memberId}/telephony/disable

Отключить телефонию участнику: снимает оба SIP-профиля.

GET/v1/companies/current/members/{memberId}/telephony/credentials

Креды внешнего SIP-профиля участника для админа (без входа под ним) — host/порты/кодеки/provisioning_uri; браузерный пароль не отдаётся.

POST/v1/companies/current/members/{memberId}/telephony/reset-password

Ротация SIP-паролей участника: возвращает новый пароль браузерного профиля и заодно молча ротирует внешний.

POST/v1/companies/current/members/{memberId}/telephony/extension

Назначить/снять внутренний номер участника (3–4 цифры, уникален в компании); требует включённой телефонии.

PUT/v1/companies/current/members/{memberId}/telephony/outbound-primary

Сделать исходящую схему основной для сотрудника (`scheme_id` или `null` для автоматического выбора); сотрудник должен уже состоять в схеме.

POST/v1/companies/current/members/telephony/outbound-primary/bulk

Сделать одну исходящую схему основной сразу для списка сотрудников (до 500); не состоящие в схеме пропускаются с `not_in_scheme`.

GET/v1/softphone/credentials

Собственные креды браузерного SIP-профиля вызывающего оператора. Требует сессии/мобильного токена — company api_token получает 403.

POST/v1/softphone/reset-password

Ротация собственного браузерного SIP-пароля.

GET/v1/softphone/external-credentials

Собственные креды внешнего SIP-профиля (для настольного телефона/Zoiper/MicroSIP) с provisioning_uri для QR.

POST/v1/softphone/external/reset-password

Ротация собственного внешнего SIP-пароля; заодно ротирует и браузерный.

GET/v1/softphone/colleagues

Телефонные коллеги по компании (директория набора) с extension и статусом доступности.

POST/v1/softphone/internal-call

Внутренний звонок коллеге по callee_user_id или внутреннему номеру.

POST/v1/softphone/external-call

Исходящий звонок на внешний номер через SIP-транк компании; опциональный contact_id (строго company-scoped); лимит call-originate — 20/мин.

Примеры

Стартовая загрузка кабинета (подмножество частей)

Запрос

bash
curl "https://api.aisar.app/v1/bootstrap?parts=subscription,chat_behavior" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": {
    "parts": {
      "subscription": {
        "id": 42,
        "plan": "business",
        "status": "active"
      },
      "chat_behavior": null
    },
    "status": {
      "subscription": 200,
      "chat_behavior": 403
    },
    "errors": {
      "chat_behavior": {
        "message": "This action is unauthorized."
      }
    }
  }
}

Приглашение нового пользователя

Запрос

bash
curl -X POST https://api.aisar.app/v1/invitations \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "new.agent@example.com",
    "role": "agent"
  }'

Ответ

json
{
  "data": {
    "message": "Invitation sent.",
    "invitation": {
      "id": 77,
      "guid": "b6e2a4d0-1234-4a90-9e21-5d3f0c8a9b11",
      "invite_url": "https://my.aisar.app/invitation/b6e2a4d0-1234-4a90-9e21-5d3f0c8a9b11",
      "email": "new.agent@example.com",
      "role": "agent",
      "expires_at": "2026-03-22T10:00:00.000000Z"
    }
  }
}

Создание команды

Запрос

bash
curl -X POST https://api.aisar.app/v1/teams \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Support",
    "description": "Customer success team",
    "channel_access_mode": "custom",
    "channel_ids": [7, 9],
    "member_ids": [5, 6]
  }'

Ответ

json
{
  "data": {
    "message": "Team created.",
    "team": {
      "id": 3,
      "company_id": 1,
      "name": "Support",
      "description": "Customer success team",
      "channel_access": { "mode": "custom", "channel_ids": [7, 9] },
      "member_ids": [5, 6],
      "created_at": "2026-03-15T10:00:00.000000Z",
      "updated_at": "2026-03-15T10:00:00.000000Z"
    }
  }
}

Смена состава/каналов команды разрешена только администратору (403)

Запрос

bash
curl -X PATCH https://api.aisar.app/v1/teams/3 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "member_ids": [5, 6, 9]
  }'

Ответ

json
{
  "data": {
    "message": "Only a company admin can change channel access or team membership while team access rules are enforced.",
    "code": "channel_access_admin_only"
  }
}

Профиль и права текущего пользователя

Запрос

bash
curl https://api.aisar.app/v1/me \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": {
    "id": 12,
    "name": "Aisha",
    "lastname": "Nurlanova",
    "email": "aisha@example.com",
    "email_verified": true,
    "phone": "+12025550102",
    "phone_verified": true,
    "photo": null,
    "country_code": "KZ",
    "language": "ru",
    "timezone": "Asia/Almaty",
    "current_company_id": 3,
    "roles": ["admin"],
    "permissions": ["company.view", "conversation.view", "message.send"],
    "company": { "id": 3, "name": "Acme" },
    "memberships": [
      { "company_id": 3, "company_name": "Acme", "roles": ["admin"], "status": "active" }
    ],
    "can_edit_channel_access": true,
    "pending_invitations": []
  }
}

Обновление профиля

Запрос

bash
curl -X POST https://api.aisar.app/v1/me \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Aisha",
    "lastname": "Nurlanova",
    "phone": "+12025550102",
    "country_code": "KZ",
    "language": "ru",
    "timezone": "Asia/Almaty"
  }'

Ответ

json
{
  "data": {
    "id": 12,
    "name": "Aisha",
    "lastname": "Nurlanova",
    "email": "aisha@example.com",
    "phone": "+12025550102",
    "country_code": "KZ",
    "language": "ru",
    "timezone": "Asia/Almaty",
    "current_company_id": 3
  }
}

Смена пароля

Запрос

bash
curl -X POST https://api.aisar.app/v1/me/password \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "current_password": "YOUR_CURRENT_PASSWORD",
    "password": "YOUR_NEW_PASSWORD",
    "password_confirmation": "YOUR_NEW_PASSWORD"
  }'

Ответ

json
{
  "data": {
    "message": "Password updated."
  }
}

Обновление статуса доступности

Запрос

bash
curl -X PUT https://api.aisar.app/v1/me/availability \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "availability": "away"
  }'

Ответ

json
{
  "status": "ok"
}

Переключение активной компании

Запрос

bash
curl -X PUT https://api.aisar.app/v1/me/current-company \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": 4
  }'

Ответ

json
{
  "data": {
    "id": 12,
    "current_company_id": 4,
    "roles": ["manager"],
    "permissions": ["company.view", "conversation.view"]
  }
}

Пользователи компании

Запрос

bash
curl https://api.aisar.app/v1/users \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": [
    {
      "id": 12,
      "name": "Aisha",
      "lastname": "Nurlanova",
      "email": "aisha@example.com",
      "availability": "online",
      "last_login_at": "2026-03-20T09:14:00.000000Z"
    },
    {
      "id": 13,
      "name": "Damir",
      "lastname": "Serik",
      "email": "damir@example.com",
      "availability": "away",
      "last_login_at": "2026-03-19T18:02:00.000000Z"
    }
  ]
}

Снимок настроек уведомлений

Запрос

bash
curl https://api.aisar.app/v1/me/notification-preferences \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": {
    "channels": {
      "mail": true,
      "push": true,
      "whatsapp": true
    },
    "categories": {
      "onboarding": { "enabled": true, "label": "Onboarding", "controllable": true },
      "trial": { "enabled": true, "label": "Trial", "controllable": true },
      "billing": { "enabled": true, "label": "Billing", "controllable": true },
      "win_back": { "enabled": true, "label": "Win-back", "controllable": true },
      "engagement": { "enabled": false, "label": "Engagement", "controllable": true },
      "transactional": { "enabled": true, "label": "Transactional", "controllable": false }
    },
    "push_triggers": {
      "chat_message_new": true,
      "conversation_assigned": true,
      "call_missed": true,
      "call_incoming": true,
      "company_unassigned": false
    },
    "quiet_hours": {
      "enabled": false,
      "start": null,
      "end": null
    }
  }
}

Отказ от WhatsApp-канала биллинговых уведомлений

Запрос

bash
curl -X PUT https://api.aisar.app/v1/me/notification-preferences \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channels": {
      "whatsapp": false
    }
  }'

Ответ

json
{
  "data": {
    "channels": {
      "mail": true,
      "push": true,
      "whatsapp": false
    },
    "categories": {
      "onboarding": { "enabled": true, "label": "Onboarding", "controllable": true },
      "trial": { "enabled": true, "label": "Trial", "controllable": true },
      "billing": { "enabled": true, "label": "Billing", "controllable": true },
      "win_back": { "enabled": true, "label": "Win-back", "controllable": true },
      "engagement": { "enabled": false, "label": "Engagement", "controllable": true },
      "transactional": { "enabled": true, "label": "Transactional", "controllable": false }
    },
    "push_triggers": {
      "chat_message_new": true,
      "conversation_assigned": true,
      "call_missed": true,
      "call_incoming": true,
      "company_unassigned": false
    },
    "quiet_hours": {
      "enabled": false,
      "start": null,
      "end": null
    }
  }
}

Тихие часы и push о неразобранных диалогах

Запрос

bash
curl -X PUT https://api.aisar.app/v1/me/notification-preferences \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "push_triggers": {
      "company_unassigned": true
    },
    "quiet_hours": {
      "enabled": true,
      "start": "22:00",
      "end": "08:00"
    }
  }'

Ответ

json
{
  "data": {
    "channels": {
      "mail": true,
      "push": true,
      "whatsapp": true
    },
    "categories": {
      "onboarding": { "enabled": true, "label": "Onboarding", "controllable": true },
      "trial": { "enabled": true, "label": "Trial", "controllable": true },
      "billing": { "enabled": true, "label": "Billing", "controllable": true },
      "win_back": { "enabled": true, "label": "Win-back", "controllable": true },
      "engagement": { "enabled": false, "label": "Engagement", "controllable": true },
      "transactional": { "enabled": true, "label": "Transactional", "controllable": false }
    },
    "push_triggers": {
      "chat_message_new": true,
      "conversation_assigned": true,
      "call_missed": true,
      "call_incoming": true,
      "company_unassigned": true
    },
    "quiet_hours": {
      "enabled": true,
      "start": "22:00",
      "end": "08:00"
    }
  }
}

Список операторов компании

Запрос

bash
curl https://api.aisar.app/v1/companies/current/members/telephony \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": [
    {
      "user_id": 12,
      "name": "Aisha Nurlanova",
      "email": "aisha@example.com",
      "photo": null,
      "telephony_enabled": true,
      "browser_registered": true,
      "extension": "101",
      "external_enabled": true,
      "external_registered": true,
      "outbound": {
        "schemes": [
          { "id": "0199a1e2-7c3a-7f4e-9c2b-6a1d8e7b1c31", "name": "Продажи", "via": "personal", "channel_ids": [412] },
          { "id": "0199a1e2-8d4b-7f4e-9c2b-6a1d8e7b1c99", "name": "Вечерние обзвоны", "via": "team", "channel_ids": [413] }
        ],
        "primary_scheme_id": "0199a1e2-7c3a-7f4e-9c2b-6a1d8e7b1c31",
        "primary_source": "explicit",
        "falls_back_to": null
      }
    },
    {
      "user_id": 13,
      "name": "Damir Serik",
      "email": "damir@example.com",
      "photo": null,
      "telephony_enabled": false,
      "browser_registered": false,
      "extension": null,
      "external_enabled": false,
      "external_registered": false,
      "outbound": {
        "schemes": [],
        "primary_scheme_id": null,
        "primary_source": null,
        "falls_back_to": "legacy"
      }
    }
  ]
}

Включить телефонию участнику

Запрос

bash
curl -X POST https://api.aisar.app/v1/companies/current/members/13/telephony/enable \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": {
    "message": "Telephony enabled.",
    "telephony": {
      "user_id": 13,
      "sip_username": "agent-13-web",
      "status": "active"
    }
  }
}

SIP-креды участника (вид админа)

Запрос

bash
curl https://api.aisar.app/v1/companies/current/members/13/telephony/credentials \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Ответ

json
{
  "data": {
    "sip_username": "agent-13",
    "sip_password": "Qw7Lm2VtRk4x",
    "host": "sip.aisar.app",
    "tcp_port": 5060,
    "tls_port": 5061,
    "default_transport": "TCP",
    "codecs": ["opus", "PCMU", "PCMA"],
    "media_encryption": "SRTP/SDES (over TLS)",
    "registered": false,
    "provisioning_uri": "sip:agent-13:Qw7Lm2VtRk4x@sip.aisar.app:5060;transport=tcp"
  }
}