Skip to content
mxApi
Единая точка входа публичного API для MODX Revolution 2 и 3 — маршруты под своим префиксом, bearer-токены, scope поверх прав MODX, каталог эндпоинтов и OpenAPI из живого реестра.
  1. Компоненты
  2. mxApi
  3. Работа с API

Токены и аутентификация

Все запросы к API, кроме самого выпуска токена, требуют заголовка:

Authorization: Bearer <token>

Токен выдаёт POST /auth/token. Способа два, выбираются параметром grant_type.

Что общего у обоих способов

  • scope обязателен. Это список запрашиваемых scope через пробел (meta.read, orders.read…). Какие scope есть на конкретном сайте — видно в каталоге эндпоинтов. Неизвестный scope → invalid_scope.
  • Токен не может быть шире прав пользователя. У пользователя, от чьего имени выдаётся токен, должно быть право mxapi_auth_token и право MODX, соответствующее каждому запрошенному scope. Иначе — insufficient_permission.
  • Открытый токен существует ровно один раз — в ответе на выпуск. В базе лежит только sha256-хэш, восстановить токен нельзя.
  • Токен непрозрачный (не JWT) и проверяется по базе: отзыв срабатывает мгновенно.
  • Минимальное время жизни — 60 секунд: более короткий токен не пережил бы собственную выдачу.

Способ 1. Логин и пароль

grant_type=password — токен по учётным данным пользователя MODX. Заводить заранее ничего не нужно.

bash
curl -X POST 'https://site.ru/mxapi/v1/auth/token' \
  -H 'Content-Type: application/json' \
  -d '{
        "grant_type": "password",
        "username": "api_user",
        "password": "********",
        "scope": "meta.read"
      }'

grant_type можно не передавать — password подразумевается по умолчанию.

Когда уместен: ручная проверка, отладка, разовые вызовы, сценарии, где пароль вводит живой человек.

Чего у такого токена нет. Клиента за ним не стоит, поэтому не работают ни собственное время жизни, ни IP-фильтр, ни свой лимит частоты, ни allow-list контекстов: время жизни берётся общее (mxapi.token_ttl), а ограничен токен только правами самого пользователя. Плюс вызывающая система вынуждена хранить пароль живого пользователя MODX, и смена пароля ломает интеграцию.

Способ 2. Клиент интеграции

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

bash
curl -X POST 'https://site.ru/mxapi/v1/auth/token' \
  -H 'Content-Type: application/json' \
  -d '{
        "grant_type": "client_credentials",
        "client_id": "<client_key>",
        "client_secret": "<client_secret>",
        "scope": "meta.read orders.read"
      }'

client_id и client_key

В запросе поле называется client_id, а в админке оно показано как client_key — это одно и то же значение.

Клиент интеграции

Клиент — машинная учётка поверх пользователя MODX: своя пара ключей, свой набор scope, своё время жизни токена. Заводится в админке, на странице того пользователя, от чьего имени будет работать интеграция:

  1. Пользователи → нужный пользователь → вкладка «mxApi»;
  2. Добавить. В окне указываются:
    • название — для людей: видно в списке клиентов и в журнале;
    • scope — списком, сгруппированным по источникам: можно отметить весь источник или отдельные scope. Предлагаются только те, на которые есть права у этого пользователя: выдать больше его прав всё равно нельзя;
    • время жизни токена — как на сайте (mxapi.token_ttl), бессрочно или своё значение в секундах;
  3. после сохранения показываются client_key и client_secret. Секрет отображается один раз и в базе не хранится (только хэш). Не скопировали — остаётся перевыпуск.

Вкладка видна тем, у кого есть право save_user: кто может сменить пользователю пароль, тот и так выпустит токен от его имени.

В строке клиента доступны изменение, перевыпуск секрета, включение/отключение и удаление.

Перевыпуск секрета

Перевыпуск даёт новый client_secret и по умолчанию не гасит уже выданные токены: плановая ротация не должна ронять работающую интеграцию. Если секрет скомпрометирован — отметьте отзыв выданных токенов в окне перевыпуска: тогда старые токены умирают сразу.

Отключение клиента останавливает всё немедленно: и выпуск новых токенов, и работу уже выданных.

Поля клиента, которых нет в форме

Правятся напрямую в таблице modx_mxapi_client. Принимают массив, JSON или строку через запятую/пробел.

ПолеСмысл
allowed_ipsБелый список адресов и подсетей CIDR (IPv4 и IPv6), например 203.0.113.10, 10.0.0.0/8. Пусто — ограничения нет. Проверяется и при выпуске токена, и при каждом вызове: адрес, потерявший доступ, перестаёт работать, не дожидаясь истечения токена. Несовпадение → ip_not_allowed
contextsКонтексты MODX, разрешённые клиенту. Пусто — только контекст по умолчанию (mxapi.context), * — любой. Нужно на мультисайте: без списка токен интеграции одного сайта работал бы на всех. Несовпадение → context_not_allowed
rate_limitСвой лимит запросов в минуту; 0 — общий (mxapi.rate_limit_per_minute)
scopesПустой список означает любой scope (в пределах прав пользователя), а не «ни одного». Форма админки пустым его не оставляет

X-Forwarded-For и IP-фильтр

За балансировщиком или CDN реальный адрес приходит в X-Forwarded-For, но mxApi учитывает этот заголовок только для адресов из настройки mxapi.trusted_proxies — иначе клиент подделал бы адрес и обошёл фильтр. Если фильтр по IP используется за прокси, перечислите прокси в настройке, иначе в allowed_ips придётся вписывать адрес самого прокси.

Время жизни токена

У клиента три режима (поле token_ttl):

РежимЗначение в базеЧто происходит
Как на сайте0берётся mxapi.token_ttl
Своё значение> 0секунды; меньше 60 не бывает
Бессрочно-1токен не истекает: expires_in: 0, expires_at: null

Свой TTL нужен потому, что одно значение на весь сайт заставляет выбирать между ночным обменом с учётной системой (нужен длинный токен) и мобильным приложением (нужен короткий).

Бессрочный режим включается явно и только для интеграций, которые невозможно научить перевыпуску: коробочный обмен, чужой скрипт, оборудование. Плата — секрет, живущий до ручного отзыва. На grant_type=password режим не распространяется: клиента там нет.

Ответ на выпуск токена

json
{
  "success": true,
  "data": {
    "access_token": "DfT2...",
    "token_type": "Bearer",
    "expires_in": 86400,
    "expires_at": "2026-07-31T09:00:00+00:00",
    "scope": "meta.read orders.read",
    "user": {}
  }
}

expires_in: 0 вместе с expires_at: null означают бессрочный токен.

Вызов эндпоинтов

bash
curl 'https://site.ru/mxapi/v1/meta/endpoints' \
  -H 'Authorization: Bearer <token>'

Заголовки, которые понимает API:

ЗаголовокНазначение
Authorization: Bearer <token>обязателен везде, кроме /auth/token
Content-Type: application/jsonтело запроса в JSON
Idempotency-Key: <строка>для изменяющих запросов: повтор с тем же ключом вернёт ответ первого успешного вызова и заголовок Idempotency-Replayed: true, операция заново не выполнится
X-MxApi-Actor: <кто>кто инициировал вызов на стороне вызывающей системы; пишется в журнал
X-MxApi-Context: <ключ>контекст MODX — только для эндпоинтов, которые это допускают, и при включённой настройке mxapi.allow_request_context. См. Права доступа → Контексты

Границы идемпотентности

Повтор возвращается из журнала, поэтому механизм работает только для изменяющих эндпоинтов и только если ответ первого вызова удалось сохранить: потоковые ответы и тела больше 256 КБ не сохраняются, и такой повтор выполнится заново. Ключ должен быть уникальным для операции — не переиспользуйте один и тот же для разных запросов.

Ответ на успешный вызов несёт заголовки лимита частоты: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. При превышении — HTTP 429 и код rate_limited; окно — одна минута, считается по клиенту (при grant_type=password — по пользователю, до аутентификации — по IP).

Списочные эндпоинты принимают limit и offset, возвращают meta.total; limit удерживается в границах mxapi.max_limit.

Отзыв токена

bash
curl -X POST 'https://site.ru/mxapi/v1/auth/revoke' \
  -H 'Authorization: Bearer <token>'

Отзывается именно тот токен, которым выполнен запрос (нужно право mxapi_auth_revoke). Чужие токены через API не отзываются — для этого есть перевыпуск секрета с отзывом токенов и отключение клиента в админке.

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

Успех:

json
{"success": true, "data": {}, "meta": {}}

Ошибка:

json
{"success": false, "error": {"code": "invalid_scope", "message": "…", "details": {}}}

Опираться нужно на error.code — он часть публичного контракта; текст message может меняться.

Коды ошибок

HTTPКодКогда
400invalid_jsonтело запроса не JSON-объект
400missing_parameterне передан обязательный параметр
400invalid_parameterпараметр не проходит проверку типа или значения
400invalid_scopeзапрошен scope, которого нет ни у одного эндпоинта
400unknown_contextконтекст MODX не найден или недоступен
401token_requiredнет заголовка Authorization: Bearer
401invalid_tokenтокен не найден, либо клиент отключён после выдачи
401token_expiredсрок действия истёк
401token_revokedтокен отозван
401credentials_requiredне переданы учётные данные при выпуске токена
401invalid_credentialsневерные логин/пароль или client_id/client_secret
403insufficient_scopeу токена нет нужного scope
403insufficient_permissionу пользователя нет права MODX
403user_inactiveпользователь неактивен или заблокирован
403ip_not_allowedадрес не входит в allowed_ips клиента
403context_not_allowedконтекст не разрешён клиенту либо выбор контекста запросом выключен
404route_not_foundнет такого эндпоинта
404not_foundобъект не найден
405method_not_allowedметод не поддерживается эндпоинтом
429rate_limitedпревышен лимит запросов в минуту
500internal_errorвнутренняя ошибка; подробности — в лог MODX, в ответ они попадают только при mxapi.debug
503service_disabledAPI выключен настройкой mxapi.enabled

Почему «нет такого пользователя» и «неверный пароль» — одна ошибка

Оба случая отвечают invalid_credentials. Иначе API превращается в проверялку существования логинов.

Журнал обращений

Каждый вызов пишется в modx_mxapi_log: клиент, пользователь, эндпоинт, контекст MODX, маршрут, метод, HTTP-статус, код ошибки, длительность, IP, значение X-MxApi-Actor и ключ идемпотентности.

Успешные чтения по умолчанию не пишутся — журнал нужен для аудита изменений и разбора отказов, а не как счётчик обращений; включаются настройкой mxapi.log_reads. Записи и любые ошибки пишутся всегда. Параметры, в имени которых есть password, secret или token, заменяются на [скрыто].

Старые записи и протухшие токены убираются автоматически, не чаще раза в час — крон не нужен. Срок хранения журнала — mxapi.log_lifetime.