Аутентификация
Один персональный токен op_ открывает API v2, API v1 и дашборд. Как его передавать, выпускать и перевыпускать, как выбирается сайт сети и какие у токена права.
Каждый запрос к API v2 несёт персональный токен OnPress: op_ и 64 шестнадцатеричных символа. Токен у пользователя один, и собственных прав у него нет — он целиком наследует права владельца, как пароль учётной записи. Тот же токен работает в API v1 и в дашборде.
Как передать токен
Заголовком Bearer или отдельным заголовком — они равнозначны:
curl -s -H "Authorization: Bearer $OP_TOKEN" "$BASE/frames?blog_id=235"
curl -s -H "X-OnPress-Key: $OP_TOKEN" "$BASE/frames?blog_id=235"
Хранится только SHA-256 токена. Потерянный токен не восстановить и не показать заново — остаётся выпустить новый.
Выпуск и ротация
Токен выпускается в профиле WordPress (wp-admin/profile.php, раздел «API Token») или через API v1, которое ради этого принимает Application Password. Поэтому первому токену не нужны ни браузер, ни WP-CLI:
# что выпущено сейчас: префикс и даты, самого токена нет
curl -s -u "$LOGIN:$APP_PASSWORD" "$SITE/wp-json/onpress/v1/api-token"
# выпустить или перевыпустить: текст токена показывается один раз
curl -s -X POST -u "$LOGIN:$APP_PASSWORD" "$SITE/wp-json/onpress/v1/api-token"
{ "token": "op_9f3c1a7e…", "prefix": "op_9f3c1a7e", "created_at": "2026-09-12 20:27:45", "last_used_at": null }
POST— это ротация, а не чтение. Выпуск — единственный способ узнать текст токена, и он неизбежно заменяет предыдущий: тот перестаёт работать сразу — у всех агентов и во всех сессиях дашборда, которые им пользовались. Отдельного отзыва нет, у пользователя одна строка. ПроверяйтеGET-ом,POSTвызывайте только ради ротации.
Результат проверки токена кэшируется в памяти сервиса на минуту. Свежевыпущенный токен работает сразу, а перевыпущенный старый может проходить ещё до минуты. Это не повод перевыпускать ещё раз: подождите или, если отзыв должен вступить в силу немедленно, перезапустите сервис. last_used_at обновляется не чаще раза в минуту на токен.
Выбор сайта сети
OnPress — сеть сайтов на одной базе, поэтому каждый запрос говорит, к какому сайту он относится. Сайт выбирает слой авторизации, а не ручка: иначе токен с доступом к одному сайту дотягивался бы параметром до другого. Порядок фиксированный:
blog_idв JSON-теле;blog_idв строке запроса;- заголовок
X-OnPress-Blog-Id; domain— в теле, в строке запроса или заголовкомX-OnPress-Domain; схема и ведущийwww.отбрасываются;- ничего не указано — блог 1, главный сайт сети.
| Параметр | Где | Тип | Что значит |
|---|---|---|---|
blog_id | тело, строка запроса, X-OnPress-Blog-Id | int | номер сайта в сети; эта документация — 233 |
domain | тело, строка запроса, X-OnPress-Domain | string | домен сайта вместо номера |
Откат на блог 1 — самая дорогая из возможных ошибок. Стенд делит базу с продом, и забытый
blog_id— это не ошибка и не пустой результат, а успешная запись в главный живой сайт.blog_id, который не является положительным целым, тоже превращается в блог 1. Указывайте сайт в каждом запросе, включая чтение.
Домен, которого нет в сети, — 404 site_not_found.
Права
Доступ к выбранному сайту проверяется членством: владелец токена должен быть пользователем этого блога. Суперадмины сети проходят на любой сайт. Нет членства — 403 forbidden_site.
Членства достаточно для страниц, фреймов, данных, стилей, меню, сайдбара и медиа. Две группы ручек требуют большего. Настройки сайта — GET и PATCH /site/settings, PUT /site/front-page, POST /site/cache/flush — только для администратора сайта (или суперадмина), иначе 403 forbidden. Создание сайта, POST /sites, — только для суперадминов сети. Подробно — Сайты и настройки.
Лимит запросов
120 запросов в минуту на пользователя в скользящем окне. Каждый авторизованный ответ несёт счётчики:
| Заголовок | Что значит |
|---|---|
X-RateLimit-Limit | лимит в минуту |
X-RateLimit-Remaining | сколько осталось в текущем окне |
X-RateLimit-Reset | Unix-время, когда окно освободится |
Retry-After | только при 429: сколько секунд подождать |
Сверх лимита — 429 too_many_requests. Счётчик ведётся на пользователя, а не на токен, поэтому перевыпуск его не сбрасывает; живёт он в памяти сервиса и обнуляется при перезапуске.
Ошибки авторизации
Они приходят в стандартной форме WordPress {code, message, data.status}, а не в форме v2 с success и error: запрос до ручки не дошёл. Сообщения у них русские — ветвитесь по code, а не по тексту. Реальные ответы:
{"code":"missing_token","message":"Нужен API-токен: заголовок Authorization: Bearer <token> или X-OnPress-Key","data":{"status":401}}
{"code":"invalid_token","message":"Неверный или отозванный API-токен","data":{"status":401}}
| Код | HTTP | Когда |
|---|---|---|
missing_token | 401 | не прислан ни Authorization: Bearer, ни X-OnPress-Key |
invalid_token | 401 | токен неизвестен или перевыпущен больше минуты назад |
forbidden_site | 403 | пользователь не состоит в выбранном сайте |
site_not_found | 404 | domain не называет ни одного сайта сети |
too_many_requests | 429 | больше 120 запросов в минуту |
auth_error | 500 | токен не удалось проверить; подробности в логе сервиса |
Полный список кодов — в разделе Ошибки.
Токен в API v1 и в дашборде
API v1 принимает тот же токен op_ через адаптер в mu-плагине onpress-api-keys: Authorization: Bearer op_… или X-OnPress-Key на любом запросе к /wp-json/. Он пускает суперадмина сети или члена того сайта, к которому обращён запрос, и отвечает теми же кодами — 401 invalid_token, 403 forbidden_site. Токен авторизует только запросы к API: посетитель от него не становится залогиненным на страницах сайта. Application Password для v1 продолжает работать и остаётся способом выпустить токен без браузера.
Дашборд входит тем же токеном и ходит с ним в оба API. Перевыпуск токена разлогинивает все сессии дашборда.
Единственная ручка без токена
curl -s "$BASE/health"
{"ok":true,"db":true}
db — настоящий пинг базы; если база не отвечает, ручка возвращает 503 с "db": false.