# Moventis → SIM Bridge Base URL: `https://api.simbridge24.com`. Полная спецификация: `https://api.simbridge24.com/openapi.json`. Кабинет: `https://cabinet.simbridge24.com/app`. Корпоративный ключ выдаёт администратор через Google-вход → «Корпоративный доступ». Ключ храните на backend Moventis, не в браузере клиента и не в APK. ## Подключение компании и телефона прямо из Moventis 1. `POST /v1/workspaces` с корпоративным ключом и JSON `{name,externalId,emails}`. В `externalId` передавайте стабильный ID компании Moventis. Повтор вернёт ту же компанию (200); новый объект — 201. Email можно не передавать; приглашение администратора требует всех разрешений компании, включая `members:write`. 2. Сохраните `id` как `simBridgeWorkspaceId` компании Moventis. 3. `POST /v1/devices/pairings`, заголовки `x-api-key` и `X-Workspace-Id`, JSON `{name}`. Ответ `{code,workspaceId,expires}`. В UI Moventis покажите код, адрес сервера и ссылку APK. Код действует 10 минут и потребляется один раз. 4. Клиент вводит код в APK на своём рабочем телефоне. Пароль Google/API-ключ ему для APK не требуется. 5. `GET /v1/devices` с тем же `X-Workspace-Id` показывает телефоны компании, `online`, `lastSeen`, `sendIntervalSeconds`. 6. Подключите выбранный `deviceId` к настройкам SMS-уведомлений компании в Moventis. Пример готового клиента: ```js import {SimBridgeClient} from './integrations/moventis-client.mjs'; const corporate = new SimBridgeClient({apiKey: process.env.SIMBRIDGE_ENTERPRISE_KEY}); const company = await corporate.createCompany({companyId: 'moventis-42', name: 'Moving Company'}); const sms = corporate.forCompany(company.id); const pairing = await sms.createPairing('Рабочий телефон диспетчера'); const devices = await sms.listDevices(); ``` ## Уведомления по заказам ```js await sms.sendOrderNotification({ deviceId: 'PHONE_ID', phoneNumber: '+972501234567', message: 'Ваш заказ №123 подтверждён. Время приезда: 10:00.', orderId: '123', event: 'confirmed', revision: 1 }); ``` Клиент ставит стабильный `Idempotency-Key: order:123:confirmed:1` и metadata заказа. Повтор с теми же данными возвращает исходное SMS. Для изменённого уведомления увеличьте revision. У разных компаний пространство ключей независимое. Вручную: `POST /v1/sms/send`, JSON `{deviceId,phoneNumber,message,externalId?,metadata?}`. Международный номер `+...`, текст 1–1600 символов, metadata до 2 КБ. `202` означает queued, а не доставку. `GET /v1/messages/{id}` возвращает статус. `POST /v1/messages/{id}/cancel` отменяет только queued. `GET /v1/messages?limit=50&direction=outbound&externalId=order:123:confirmed:1` возвращает `{items,nextCursor}`. Следующая страница: `cursor=`. Есть фильтры status/deviceId/direction/externalId. До 100 строк на страницу. `unknown` не следует автоматически повторять: модем мог уже отправить SMS. ## Доступы | Кто | Возможности | |---|---| | Администратор сервиса через Google | Все компании, выдача и отзыв корпоративных доступов | | Корпоративное приложение | Только его компании; создание/просмотр компаний, подключение телефонов и прочие действия согласно scopes | | Google-пользователь компании | Администратор только приглашённых компаний | | API-ключ компании | Одна компания и заданные scopes | | Токен телефона | Только собственные задания, результаты и входящие события | Scopes: `workspaces:create`, `workspaces:read`, `devices:read`, `devices:write`, `sms:send`, `sms:read`, `keys:write`, `members:write`, `webhook:write`, `audit:read`. Срок нового ключа по умолчанию 90 дней, `expiresInDays` от 1 до 365. По умолчанию корпоративный ключ получает создание/чтение компаний, управление устройствами, отправку/чтение SMS. Если нужно приглашать Google-администраторов, администратор выдаёт полный набор прав компании; ограниченный ключ не может повысить права через приглашения. `GET/POST /v1/enterprise/apps`, `POST /v1/enterprise/apps/{id}/keys`, `DELETE /v1/enterprise/apps/{id}/keys/{keyId}`, `DELETE /v1/enterprise/apps/{id}` доступны только администратору сервиса. Отзыв приложения также отзывает ключи и телефоны его компаний, удаляет неиспользованные коды и останавливает очередь ожидающих SMS. Отзыв отдельного корпоративного ключа не отзывает другие ключи приложения. `GET/POST /v1/keys` и `DELETE /v1/keys/{id}` управляют ключами одной компании. Новые ключи не могут получить больше scopes, чем выдавший ключ. `PUT /v1/workspaces/{id}/members` заменяет список Google email администраторов компании. Удалённый аккаунт теряет доступ немедленно; если у него есть членство в другой компании, оно сохраняется. `PATCH /v1/devices/{id}` принимает `{name?,sendIntervalSeconds?}` (5–3600). `DELETE /v1/devices/{id}` отключает телефон, отзывает токен, переводит queued SMS в failed. `GET /v1/audit` — последние 100 действий компании. ## Webhook `PUT /v1/webhook` с `{url:'https://BACKEND/events'}` включает webhook и возвращает секрет один раз. `{url:''}` отключает; `rotateSecret:true` меняет секрет. Требуется публичный HTTPS, порт 443. `GET /v1/events` показывает последние 100 событий; `POST /v1/events/{id}/retry` повторяет failed/disabled. В готовом клиенте есть `verifyWebhook(rawBody, signature, secret, {workspaceId})`: проверяет подпись и принадлежность события компании. Подпись: `X-SimBridge-Signature` равна `sha256=` плюс HMAC-SHA256 исходного тела запроса с секретом компании. Проверяйте подпись в постоянном времени, до JSON-разбора. Событие `{id,type,createdAt,data}`; `data` — SMS с workspaceId/deviceId/externalId/metadata. Принимать только ожидаемый workspaceId. Обработчик должен устранить дубли по id и быстро вернуть 2xx. До 8 попыток с увеличением задержки; доставка не гарантирована при постоянной ошибке вашего endpoint. В Moventis также проверяйте свою авторизацию: компания берётся из серверной сессии клиента, а не из переданного браузером workspaceId. Корпоративный ключ имеет доступ ко всем компаниям своего приложения; только backend Moventis должен им пользоваться. ## Ошибки и лимиты 400 некорректные данные; 401 неверный/истёкший/отозванный ключ; 403 чужая компания или scope; 404 нет объекта в своей компании; 409 конфликт/неподходящий статус; 429 лимит (Retry-After); 503 временная недоступность. Повторять сетевые ошибки/5xx при отправке можно с тем же Idempotency-Key. 4xx автоматически не повторять. 600 запросов в минуту на ключ/сессию; 100 queued SMS на телефон. Списки: компании до 500, устройства до 200, ключи компании до 100; история SMS постраничная. Для большого объёма требуется увеличить ресурсы сервера и согласовать лимиты. ## Подключение телефона через QR `POST /v1/devices/pairings` с ключом компании и разрешением `devices:write`, тело `{ "name": "Рабочий Android" }`, возвращает HTTP 201: - `code` — одноразовый код для ручного ввода; - `expires` — срок действия в миллисекундах Unix, 10 минут; - `workspaceId` — компания, в которой будет подключён телефон; - `serverUrl` — адрес сервера из API_BASE_URL; - `qrPayload` — строка JSON `{type:"simbridge-pairing",version:1,serverUrl,code,expires}`; - `qrImageDataUrl` — готовая PNG-картинка `data:image/png;base64,...` для `src` изображения. Moventis или другое приложение вызывает API своим backend и передаёт результат только авторизованному пользователю своей компании. Можно показать готовую картинку или самостоятельно закодировать `qrPayload` в QR. Не размещайте код в публичных URL, логах или аналитике; ответ сервера имеет Cache-Control: no-store. При строгом CSP вашего приложения разрешите изображения data: либо используйте свой локальный генератор QR. В APK 0.4 нажмите «Сканировать QR-код», разрешите камеру и наведите её на экран. Сканирование локальное, без стороннего сервиса. Приложение проверяет формат и срок действия, заполняет адрес сервера и код. Проверьте адрес и нажмите «Подключить устройство». После подключения запустите шлюз. Ручной ввод адреса и кода остаётся доступным, включая телефоны без камеры. QR и ручной ввод используют один код: после успешного подключения повторное использование невозможно. Истёкший код создавайте заново.