YFG ID API v1 OAuth 2.0 · OpenID Connect

Документация для разработчиков

Единый вход, публичный каталог мероприятий и мастеров, продажа билетов, пользовательские данные и вебхуки. Одна интеграция — весь YFG.

Issuer
https://yfg.su/id
API
https://yfg.su/id/api
Формат
JSON UTF-8 · рубли
Версия
X-API-Version: 1
01

Что такое YFG ID

YFG ID — провайдер входа по OAuth 2.0 / OpenID Connect и публичная платформа данных YFG. Партнёр может впустить пользователя на свой сайт, показать афишу и каталог мастеров, продать билет во встроенном виджете и получать изменения через вебхуки.

Вход и профиль

Authorization Code + PKCE, ID token, userinfo. Пользователь сам подтверждает доступ.

Витрина и продажи

Публичный каталог, остатки по типам билетов, статус отмены и виджеты покупки.

События в реальном времени

Вебхуки с подписью HMAC-SHA256: создание, публикация, изменение и отмена.

Базовые адреса

НазначениеАдрес
Issuerhttps://yfg.su/id
Discoveryhttps://yfg.su/id/.well-known/openid-configuration
JWKShttps://yfg.su/id/.well-known/jwks.json
Публичный APIhttps://yfg.su/id/api
OpenAPI 3.1https://yfg.su/id/api/openapi.json
Кабинет приложений/id/apps
Публичные данные доступны без логина пользователя. Для каталога используйте client_credentials или HTTP Basic с client_id и client_secret. Пользовательская сессия нужна только для /api/me/*.

Запросы из браузера к /id/api/* разрешены только с origin, добавленных в «Разрешённые адреса» приложения. Для остальных origin CORS-заголовки не возвращаются.

02

Быстрый старт

  1. Создайте приложение в кабинете YFG ID. Укажите название, сайт, redirect_uri и разрешённые origin для виджетов. Секрет показывается один раз — сохраните его на сервере.
  2. Выберите сценарий. Вход пользователя — Authorization Code + PKCE. Серверная витрина — client_credentials или Basic. Встраивание на страницу — виджет.
  3. Получите токен или авторизуйте приложение. Для каталога достаточно Basic, для пользовательских данных нужен access token.
  4. Вызовите API. Каталог — https://yfg.su/id/api/events и /api/masters; данные пользователя — /api/me/*.
  5. Подпишитесь на вебхуки в карточке приложения, чтобы получать изменения мероприятий без polling.

Сервер-сервер за минуту

curl -X POST https://yfg.su/id/token \
  -d grant_type=client_credentials \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d scope="events:read masters:read"
curl "https://yfg.su/id/api/events?when=upcoming&per_page=20" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Basic-авторизация работает только для публичного каталога и только для конфиденциальных приложений. client_secret никогда не попадает в браузер.

03

Вход и OAuth 2.0

Провайдер поддерживает Authorization Code, Refresh Token и Client Credentials. Для браузерных приложений используйте Authorization Code с PKCE S256; implicit flow намеренно не поддерживается.

МетодEndpointНазначение
GET/authorizeЭкран входа и согласия
POST/tokenКод, refresh или машинный токен
GET/userinfoПрофиль по access token
GET/.well-known/openid-configurationМетаданные OIDC
GET/.well-known/jwks.jsonПубличные ключи RS256
GET/client/<client_id>Публичная карточка приложения
POST/revoke_tokenОтзыв access или refresh
POST/introspectПроверка состояния токена
GET/end_sessionВыход из YFG ID

Шаг 1. Отправьте пользователя на вход

https://yfg.su/id/authorize?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://partner.example/callback
  &scope=openid%20profile%20email
  &state=RANDOM_STATE
  &nonce=RANDOM_NONCE
  &code_challenge=BASE64URL_SHA256_VERIFIER
  &code_challenge_method=S256
  • response_type — только code.
  • state обязателен для защиты от CSRF, nonce — для ID token.
  • prompt=consent или force_confirm=1 повторно показывает согласие; login_hint подсказывает логин.
  • Код живёт 5 минут, одноразовый и привязан к client_id, redirect_uri и PKCE.

Шаг 2. Обменяйте код на токены

curl -X POST https://yfg.su/id/token \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=CODE_FROM_CALLBACK \
  -d redirect_uri=https://partner.example/callback \
  -d code_verifier=ORIGINAL_VERIFIER
{
  "access_token": "yfg_at_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "yfg_rt_...",
  "scope": "openid profile email",
  "id_token": "eyJhbGciOiJSUzI1NiIs..."
}

Публичное приложение не отправляет секрет, но обязано передать client_id и code_verifier. Поле scope возвращается, если выданных прав меньше, чем у приложения; id_token приходит при scope openid.

Шаг 3. Прочитайте профиль

curl https://yfg.su/id/userinfo \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Ответ содержит стандартные OIDC-поля и совместимые с Яндекс ID id, login, display_name, default_email, emails, is_avatar_empty — в пределах выданных scope.

Client Credentials

Только для конфиденциальных приложений и только для events:read / masters:read. Пользовательские права в машинном токене запрещены. Refresh-токен не выдаётся.

curl -X POST https://yfg.su/id/token \
  -d grant_type=client_credentials \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d scope="events:read masters:read"

Если scope не передан, выдаётся пересечение разрешённых приложению каталоговых прав. Для Basic-авторизации каталога токен не нужен: -u "client_id:client_secret".

Проверяйте ID token. Подпись — RS256 по https://yfg.su/id/.well-known/jwks.json; также проверяйте iss, aud, exp и nonce. Не доверяйте данным из токена, пока подпись не проверена.
04

Токены и профиль

ТокенСрокОсобенности
Код авторизации5 минутОдноразовый; повторное использование отзывает всю семью токенов
Access token1 часВ базе только SHA-256-хеш; передаётся в Authorization: Bearer
Refresh token30 днейРотация при каждом обновлении; повторное использование — признак кражи
Машинный access token1 часclient_credentials, без refresh и без /api/me/*
ID token (JWT)1 часRS256, kid в заголовке, ключи в JWKS

Обновление и отзыв

curl -X POST https://yfg.su/id/token \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token=YOUR_REFRESH_TOKEN

При обновлении сохраните новый refresh token — старый сразу становится недействительным. Отзыв: POST https://yfg.su/id/revoke_token с token и token_type_hint. Выход из YFG ID: GET https://yfg.su/id/end_session с id_token_hint и post_logout_redirect_uri.

Пользователь может посмотреть и отозвать доступы в кабинете YFG ID → «Подключённые приложения». После отзыва все токены приложения перестают работать.

05

Права доступа

Права запрашиваются через scope и проверяются на каждом endpoint. Неизвестное право — ошибка invalid_scope, а не молчаливое отбрасывание.

ScopeГруппаЧто открываетClaims
openidВходИдентификатор пользователя subsub
profileВходИмя, ник, аватар, город, статусы проверкиname, preferred_username, picture, locale, organizer_verified, master_verified
emailВходПочта аккаунтаemail, email_verified
events:readМероприятияКаталог, карточки, остатки по типам, отмена—
tickets:readБилетыПроверка статуса билета по коду—
tickets:sellБилетыВстраиваемая продажа билетов—
tickets:listБилетыБилеты пользователя—
resale:readБилетыЛоты вторичной продажи пользователя—
masters:readМаркетплейсКаталог мастеров, анкета, услуги, портфолио, слотыmaster_verified
orders:readМаркетплейсЗаказы пользователя как клиента или мастера—
waitlist:readМероприятияЗаписи в листе ожидания—

Псевдонимы принимаются для совместимости с Яндекс ID: login:info, login:email, login:avatar, login:birthday, login:default_phone, user:read, default, basic, events, tickets. Они приводятся к каноническим правам до выдачи согласия.

Машинные токены и Basic работают только с events:read и masters:read. Для /api/me/* нужен пользовательский access token.
06

Каталог мероприятий

Публичные мероприятия: published, completed и cancelled. Черновики, модерация и удалённые записи не отдаются. По умолчанию — ближайшие опубликованные.

МетодEndpointНазначение
GET/api/eventsСписок с фильтрами и пагинацией
GET/api/events/<id>Полная карточка
GET/api/events/<id>/availabilityБыстрые остатки по типам

Фильтры списка

ПараметрЗначения
qПоиск по названию, описанию, городу и адресу
categoryКатегория мероприятия
locationГород, подстрока
whenupcoming (по умолчанию), past, all
statuspublished (по умолчанию), completed, cancelled, all
date_from, date_toДата или ISO-время; только дата — московские сутки
price_min, price_maxЦена, рубли
available1 — только с доступными билетами
updated_sinceISO-время для инкрементальной синхронизации
sort, dirdate, price, title, views, sales, updated
page, per_pageСтраница и размер, до 100
curl -u "$YFGID_CLIENT_ID:$YFGID_CLIENT_SECRET" \
  "https://yfg.su/id/api/events?when=upcoming&status=all&per_page=20"
{
  "id": 123,
  "title": "Ночь настольных игр",
  "status": "published",
  "cancelled": false,
  "date": "2026-11-15T20:00:00+03:00",
  "date_label": "15.11.2026 20:00",
  "location": "Москва",
  "price": 1200,
  "price_label": "от 1 200 руб.",
  "sold_out": false,
  "ticket_counts": {
    "total": 100, "available": 37, "sold": 63, "sold_out": false
  },
  "ticket_types": [
    {"id": 1, "name": "Стандарт", "quota": 80, "available": 37,
     "sold": 43, "is_active": true, "sold_out": false},
    {"id": 2, "name": "VIP", "quota": 20, "available": 0,
     "sold": 20, "is_active": true, "sold_out": true}
  ]
}

ticket_counts считается по активным типам; если типов нет — по агрегатам мероприятия. sold не бывает отрицательным. Для отменённого события cancelled: true и заполнен cancelled_at; причина отмены — внутренняя и в API не отдаётся.

Списки поддерживают ETag и If-None-Match (304), Link, X-Total-Count и X-RateLimit-*. Ответы кэшируются как private, max-age=30.

07

Каталог мастеров

Только активные, проверенные модерацией мастера с активным аккаунтом. Скрытые и забаненные пользователи не попадают в каталог.

МетодEndpointНазначение
GET/api/mastersСписок с фильтрами
GET/api/masters/<id>Анкета, услуги, портфолио, слоты

Фильтры

ПараметрЗначения
qИмя, ник, заголовок, описание, город
specializationКлюч специализации, например artist
languageКлюч языка, например ru
cityГород, подстрока
available, verified1 — только доступные / проверенные
ratingМинимальный рейтинг
price_min, price_maxЦена услуг, рубли
updated_sinceISO-время для синхронизации
sort, dirrating, reviews, orders, price, created, name, updated
{
  "id": 7,
  "user_id": 42,
  "username": "master",
  "display_name": "Мастер",
  "headline": "Иллюстрации и персонажи",
  "specializations": ["artist"],
  "rating": 4.9,
  "review_count": 12,
  "is_verified": true,
  "is_available": true,
  "price_from": 1500,
  "price_label": "от 1 500 руб.",
  "services_count": 3,
  "url": "https://yfg.su/user/42"
}

Детальная карточка добавляет services, portfolio (до 24 работ), slots (до 30 будущих свободных окон), review_summary, время ответа и ссылки на соцсети. Email и другие приватные данные не отдаются.

08

Данные пользователя

Пять ресурсов /api/me/* требуют пользовательский Bearer-токен и соответствующий scope. Машинные токены и Basic здесь не работают.

EndpointScopeЧто возвращает
/api/me/ticketstickets:listБилеты: событие, тип, статус, цена
/api/me/mastermasters:readАнкета мастера и активные услуги; без пагинации
/api/me/ordersorders:readЗаказы как клиента и как мастера
/api/me/waitlistwaitlist:readЛист ожидания пользователя
/api/me/resalesresale:readЛоты вторичной продажи
curl "https://yfg.su/id/api/me/tickets?page=1&per_page=50" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN"

Коллекции поддерживают page и per_page (до 100, по умолчанию 100 для обратной совместимости). Ответ: items + pagination + meta.api_version. /api/me/master возвращает объект {"master": ...} или {"master": null}.

09

Виджеты

Виджет — самый быстрый способ показать афишу, билет, кнопку входа или профиль. Покупка открывается в попапе YFG: деньги и билеты остаются на стороне YFG, а сайт получает событие purchase.

<script src="https://yfg.su/static/js/yfgid_widget.js"
        data-yfgid-client="YOUR_CLIENT_ID"
        data-yfgid-theme="auto"></script>

<div data-yfgid-events="123,456" data-yfgid-columns="2"></div>
<div data-yfgid-buy="123" data-yfgid-label="Купить билет"></div>
<div data-yfgid-login></div>
АтрибутНазначение
data-yfgid-eventКарточка одного мероприятия с покупкой
data-yfgid-eventsВитрина из нескольких id через запятую
data-yfgid-buyКомпактная кнопка «Купить билет»
data-yfgid-loginКнопка входа; варианты light, dark, icon
data-yfgid-profileКарточка профиля после YFGID.setToken()
data-yfgid-shapecard или ticket — вид билета с перфорацией
data-yfgid-sizes, m, l
data-yfgid-themelight, dark, auto
data-yfgid-columnsКолонки витрины, 1–4
data-yfgid-clientclient_id, если не задан на <script>
data-yfgid-scopeScope для кнопки входа
data-yfgid-login-urlСвой адрес входа вместо стандартного

JavaScript API

МетодНазначение
YFGID.renderAll()Отрисовать все блоки на странице
YFGID.refresh()Сбросить кэш и перечитать данные
YFGID.on('purchase', fn)Успешная или неуспешная покупка в попапе
YFGID.on('login', fn)Перехват входа; return false отменяет стандартный переход
YFGID.setToken(token)Передать access token для профиля
YFGID.openCheckout(id)Открыть покупку программно
YFGID.loginUrl(host)Получить URL входа
YFGID.on('purchase', function (payload) {
  console.log('покупка', payload.event_id, payload.ok);
});
YFGID.on('login', function () { /* свой сценарий входа */ });
YFGID.refresh();

Домен страницы должен быть в «Разрешённых адресах» приложения. В консоли браузера виджет подскажет точный origin, который нужно добавить.

10

Вебхуки

Подписки настраиваются в карточке приложения. До 5 endpoint'ов на приложение, только HTTPS. Секрет whsec_... показывается один раз и хранится в зашифрованном виде. Там же можно повернуть секрет, удалить подписку и посмотреть последние доставки.

СобытиеКогда приходит
event.createdМероприятие создано и опубликовано
event.publishedЧерновик опубликован
event.updatedИзменены публичные поля
event.cancelledМероприятие отменено
event.completedМероприятие завершено
event.deletedМероприятие удалено
{
  "id": "evt_...",
  "type": "event.cancelled",
  "api_version": "1",
  "created_at": "2026-10-28T00:00:00+03:00",
  "data": { "id": 123, "status": "cancelled", "cancelled": true, "...": "..." }
}
ЗаголовокЗначение
X-YFG-EventТип события
X-YFG-DeliveryID доставки
X-YFG-TimestampUnix-время подписи
X-YFG-Signaturesha256=... от строки timestamp.body
X-YFG-Api-Version1
import hashlib
import hmac

message = f"{timestamp}.{raw_body}".encode("utf-8")
expected = "sha256=" + hmac.new(
    secret.encode("utf-8"), message, hashlib.sha256
).hexdigest()
assert hmac.compare_digest(expected, signature)

Отвечайте 2xx быстро; тело можно обрабатывать асинхронно. 410 Gone отключает endpoint. Ошибки и таймауты повторяются до 8 попыток; паузы — 60 секунд, 5, 30 минут, 2, 6, 12 и 24 часа. После 20 последовательных сбоев подписка отключается.

Проверяйте timestamp и подпись до разбора JSON. Рекомендуемое окно — 5 минут; старые подписи отклоняйте.
11

Лимиты и ошибки

ОперацияЛимит
Каталог и /api/me/*240 запросов / 10 минут на приложение
Authorize120 / 10 минут на IP, 60 — на клиента
Token60 / 10 минут на IP, 120 — на клиента
Userinfo240 / 10 минут на клиента
Виджет600 / 10 минут на origin
Покупка в попапе30 / 10 минут, до 50 билетов за раз

Каждый ответ API содержит X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset и X-RateLimit-Window. При 429 приходит Retry-After.

КодКогдаЧто делать
invalid_requestНет параметра или он некорректенИсправить запрос
invalid_clientНеверный client_id или секретПроверить ключи
invalid_grantКод/refresh истёк или отозванНачать вход заново
invalid_scopeПраво неизвестно или не разрешеноИсправить scope в кабинете
invalid_tokenТокен истёк, отозван или не тот типОбновить токен
insufficient_scopeНет нужного праваЗапросить право у пользователя
not_foundОбъект скрыт или не существуетНе повторять запрос
temporarily_unavailableRate limit или перегрузкаПовторить после Retry-After

Ошибки API имеют вид {"error": "...", "error_description": "...", "request_id": "..."}. При обращении к API сохраняйте request_id — по нему быстрее разобрать инцидент.

12

Версии и поддержка

Текущая версия API — 1. Она передаётся в X-API-Version, meta.api_version и X-YFG-Api-Version вебхуков. Обратно совместимые изменения (новые поля и фильтры) выходят без смены версии; ломающие — только в новой версии API.

Ограничения версии 1

Вебхуки сейчас покрывают только мероприятия; управление подписками — через кабинет, без публичного API. Каталог только для чтения. Пагинация — page/per_page, без cursor-режима. Песочницы нет: для тестов используйте отдельное приложение и реальные тестовые мероприятия.

Нужна помощь? Начните с кабинета приложений и логов в карточке приложения: там видны входы, токены и ошибки.

Оплата недоступна

Мы подключаем приём платежей и вернёмся совсем скоро. Билеты и заказы можно будет оплатить сразу после запуска — следите за обновлениями.