Gym One · публичное API · версия 1

Документация API Gym One

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

https://api.gym-one.ru/public/v1 JSON · UTF-8 Bearer-ключ openapi.json
Ключ не указан
Ключ нужен для кнопок «Отправить» ниже. Он не сохраняется: хранится только в этой вкладке и пропадает при перезагрузке страницы. Запросы на запись меняют настоящие данные клуба.

Начало работы

  1. Владелец клуба выпускает ключ в портале: Настройки → API → «Выпустить ключ». Ключ показывается один раз, сохраните его.
  2. Выберите права: только чтение или чтение и запись. Для интеграции, которая только забирает данные, хватит чтения.
  3. Отправляйте запросы на https://api.gym-one.ru/public/v1 с заголовком Authorization: Bearer <ключ>.
curl https://api.gym-one.ru/public/v1/visitors?limit=10 \
  -H "Authorization: Bearer $G1_API_KEY"
Ключ даёт доступ только к данным клуба, для которого выпущен: клуб определяется ключом, в запросе его не передают. Храните ключ на сервере и не встраивайте его в код сайта или мобильного приложения. Если ключ утёк, отзовите его в настройках и выпустите новый.

Персональные данные

Через API передаются персональные данные посетителей и тренеров клуба: имя, телефон, эл. почта, дата рождения, пол, абонементы, посещения, записи. По Федеральному закону № 152-ФЗ «О персональных данных» оператор этих данных — клуб. Gym One обрабатывает их по поручению клуба (п. 2.4 лицензионного соглашения). Интегратор — тот, кому клуб передал ключ, — тоже обрабатывает данные по поручению клуба (ч. 3 ст. 6 закона, п. 3.13 соглашения).

Что сделать клубу до передачи ключа интегратору:

  1. Заключить с интегратором договор с поручением на обработку персональных данных. По ч. 3 ст. 6 в нём определяются: перечень данных, действия с ними и цели обработки; обязанность интегратора соблюдать конфиденциальность и обеспечивать безопасность данных; требования ч. 5 ст. 18 (данные граждан России записываются и хранятся в базах данных на территории России) и ст. 18.1; обязанность по запросу клуба, в том числе до начала обработки, предоставлять документы о принятых мерах; требования к защите данных по ст. 19, в том числе обязанность уведомить клуб об утечке (случаи ч. 3.1 ст. 21).
  2. Получить согласие посетителей на поручение обработки интегратору, если оно требуется (ч. 3 ст. 6), и указать в согласии наименование или ФИО и адрес интегратора (п. 6 ч. 4 ст. 9). По запросу посетителя сообщать ему эти сведения (п. 9 ч. 7 ст. 14).
  3. Если интегратор — иностранное лицо или обрабатывает данные за пределами России, выполнить требования ст. 12 о трансграничной передаче.

Интегратору: обрабатывайте данные только в целях и объёме, указанных в договоре с клубом, не передавайте их третьим лицам без поручения клуба и храните ключ в тайне. Перед посетителем за действия интегратора отвечает клуб, интегратор отвечает перед клубом (ч. 5 ст. 6).

Когда договор с интегратором прекращён или ключ утёк, клуб отзывает ключ: запросы по нему сразу получают 401, неотправленные вебхуки отменяются. Gym One ведёт журнал запросов по каждому ключу (время, метод и адрес, код ответа, IP-адрес) и хранит его 90 дней. Последние 200 запросов клуб видит в портале: Настройки → API → «Журнал».

Условия использования API — в лицензионном соглашении, п. 3.13: gym-one.ru/legal/agreement.pdf.

Авторизация

Каждый запрос, кроме openapi.json, передаёт ключ в заголовке:

Authorization: Bearer g1_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
СитуацияОтвет
Заголовка нет или ключ неверного формата401
Ключ не найден или отозван401
Запрос на запись (POST, PUT, DELETE) ключом «только чтение»403
Клуб заблокирован403

Формат данных

  • Тела запросов и ответов — JSON в UTF-8, заголовок Content-Type: application/json.
  • Время — ISO 8601 со смещением часового пояса клуба: 2026-10-01T09:00:00+03:00. Даты — YYYY-MM-DD.
  • Телефон принимается в любом формате (+7 900 123-45-67, 89001234567), от 10 до 15 цифр.
  • Неизвестные поля в теле запроса не принимаются: ответ 400.

Ошибки

Ошибки приходят в формате RFC 9457 (application/problem+json). Причина по-русски — в поле detail, instance — номер запроса для обращения в поддержку.

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json; charset=utf-8

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Нужен заголовок Authorization: Bearer <ключ API>",
  "instance": "7bdb743e-2860-4cbc-bc30-f9bbc20488e9"
}
КодЗначение
200 201 204Успех; 201 — объект создан, 204 — без тела
400Неверные параметры или тело запроса
401 / 403Нет ключа / нет прав
404Объект не найден (в том числе объект другого клуба)
409Конфликт: нет мест, посетитель уже записан, у тренера перерыв, абонемент не позволяет вход
422Связанный объект не найден или не подходит (посетитель, тип абонемента, занятие)
429Превышен лимит запросов
5xxСбой на нашей стороне, повторите запрос позже

Лимиты

На каждый ключ: 600 запросов в минуту всего, из них не больше 120 на запись. В каждом ответе есть заголовки:

RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 42

При превышении — ответ 429 и заголовок Retry-After с числом секунд до сброса.

Списки и постраничная выдача

Все списки принимают limit (1–200, по умолчанию 50), cursor и updated_since и отвечают так:

{
  "data": [ { "id": 37, "name": "Иван Петров", … } ],
  "next_cursor": "eyJ1IjoiMjAyNi0wOS0zMCAyMTozODowNCIsImkiOjM3fQ"
}

Чтобы получить следующую страницу, передайте next_cursor в параметре cursor. Когда next_cursor равен null, страниц больше нет.

updated_since (ISO 8601) возвращает только записи, изменённые начиная с этого момента. Так удобно синхронизировать изменения: запомните время последней выгрузки и в следующий раз запросите только новое.

Повтор запроса без дублей

Операции создания (POST) принимают заголовок Idempotency-Key: от 1 до 100 печатных ASCII-символов, например UUID. Повтор с тем же ключом в течение суток не создаст второй объект, а вернёт первый ответ с заголовком Idempotent-Replayed: true. Тот же ключ с другим телом запроса — ответ 422.

curl -X POST https://api.gym-one.ru/public/v1/visitors \
  -H "Authorization: Bearer $G1_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f0c2b8e-1d7a-4c1e-9a55-2f3b8c0d9e11" \
  -d '{"name":"Иван Петров","phone":"+7 900 123-45-67"}'

Вебхуки: формат и подпись

Когда в клубе происходит событие, Gym One отправляет POST на адрес, заданный через PUT /webhook. Один вебхук на ключ, только https://.

СобытиеКогдаdata
visitor.createdДобавлен посетитель (в портале, в ЛК или через API)посетитель
subscription.purchasedВыдан абонементабонемент
visit.createdОтмечено посещениепосещение
workout.enteredПосетитель пришёл на групповую тренировку или тренировку по записипосещение
lead.stage_changedЗаявка перешла на другую стадию воронки: вручную, после покупки абонемента, после первого посещения, возврат после отказа. Только с модулем «Привлечение и продажи»заявка и стадии

data события lead.stage_changed: lead_id, name, phone, email, source (callback, vitrina, yandex, site, qr, import, api, referral, manual, phone, vk, vk_ads, avito, 2gis), source_label, utm_source, utm_medium, utm_campaign, funnel — название воронки, stage_from и stage_to — {id, title, kind}, где kind: open — обычная стадия, trial — пробное, won — купил, lost — отказ; reason — причина отказа или что произошло, visitor_id — карточка посетителя, если заявка с ней связана, responsible_user_id, changed_at, created_at. У обезличенной заявки (отказ старше 12 месяцев) имени и контактов нет; у заявки из сообщений ВКонтакте или Авито телефона и почты может не быть.

POST https://example.com/g1-hook
Content-Type: application/json
X-G1-Event: visitor.created
X-G1-Delivery: 0b9d7c1e-6c1f-4f7e-8a3b-1c2d3e4f5a6b
X-G1-Signature: t=1790793484,v1=5c1f…e9a2

{
  "id": "0b9d7c1e-6c1f-4f7e-8a3b-1c2d3e4f5a6b",
  "type": "visitor.created",
  "created_at": "2026-09-30T21:38:04+03:00",
  "data": { "id": 91, "name": "Иван Петров", "phone": "79001234567", … }
}

Ответьте кодом 2xx в течение 10 секунд. Иначе доставка повторится через 1 мин, 5 мин, 30 мин, 2 ч, 6 ч, 12 ч и 24 ч. Если объект удалили до отправки, data будет null. Одно событие может прийти дважды — различайте события по id.

Проверка подписи. v1 — HMAC-SHA256 от строки t + "." + тело запроса на секрете вебхука (выдаётся один раз при первом PUT /webhook), в hex. Сверяйте подпись с сырым телом запроса, до разбора JSON, и отклоняйте запросы со старым t (например, старше 5 минут).

import crypto from 'node:crypto'

function verify (rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
  const mac = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1))
}