# Мероприятия, регистрация и запись

Source: <https://telegafirst.com/docs/events-and-booking>
Locale: ru
releaseGitSha: 0bb11ef116e17ec64c802d5476eaff442e53d252
sourceContentDigest: 2a56c9ed826ee1dad8f25749bdd67eab359c7fe0d464ba53fff2babd3d2d27cc
Version: 1

В TelegaFirst мероприятие собирает участников, а запись резервирует время и ресурс — например, кабинет или мастера. Команда микробизнеса ведёт работу в Telegram, собственный AI-агент владельца использует Hub в рамках выданных прав. Успешный результат нужно читать по объекту: регистрация, отметка посещения, ожидающая подтверждения запись и оплаченный заказ имеют разные состояния.

Это руководство показывает создание и чтение через управляющий доступ бизнеса. Клиентские публичные ссылки открываются по opaque-кодам, а кабинет покупателя дополнительно защищён его сессией. Примеры ниже содержат демонстрационные контрактные значения; реальной регистрации, бронирования, оплаты или проверки конкретного AI-клиента здесь не выполнялось.

## Доступ и номера объектов

Перед работой [подключите бизнес](https://telegafirst.com/docs/getting-started), проверьте выбранный `slug`, бота и readiness. MCP использует [авторизацию](https://telegafirst.com/docs/authorization) и актуальный `tools/list`; REST-пути ниже относительны к вашему Hub. На этих REST routes принимается допустимый ключ в `X-Api-Key` либо `Authorization: Bearer tgf_*`. JWT владельца/покупателя не подставляется вместо этого ключа.

| Действие | Scope |
| - | - |
| Список мероприятий и регистраций, отдельная регистрация | `events:read` |
| Создать/изменить мероприятие, зарегистрировать, отменить регистрацию, отметить вход | `events:write` |
| Ресурсы, расписания и поиск доступности | `booking:read` |
| Создать/отменить запись, отметить посещение, изменить ресурсы/расписания | `booking:write` |

Права, тарифное окно, готовность бота и tenant-проверки продолжают действовать после выдачи credential. Для цепочки, каталога или денежной операции понадобятся права соответствующей семьи. [Scopes и permissions](https://telegafirst.com/docs/scopes-and-permissions).

Берите номер мероприятия, пользователя, товара и ресурса из ответа управляющего API. В регистрациях REST тело содержит числовые `event_seq_num`, `user_seq_num`, при необходимости `catalog_seq_num`. Номер в пути регистрации — каноническая десятичная строка положительного int4 (`1..2147483647`): без нуля, знака, пробелов, дроби, hex или экспоненты. Записи и их ресурсы также используют guarded `seq_num`; `client_id` определяется доступом, не JSON-телом.

Ссылки, которые можно открыть без сессии, используют непрозрачный код: `/events/{code}`, `/booking/{code}`, кодовый deeplink бота или QR чекина. Номер регистрации/ресурса не является публичным кодом. В кабинете покупателя номер доступен только после проверки владения. Не строите анонимные ссылки из `seqNum`; внутренние PK никогда не передаются через публичный API. [Адреса и пагинация](https://telegafirst.com/docs/identifiers-and-pagination).

## Подготовьте мероприятие

Прочитайте `GET /api/v1/tools/events` (`200`, `events:read`). Создание — `POST /api/v1/tools/events` (`201`, `events:write`, непустой POST требует `Idempotency-Key`). Пример строгого тела:

```json
{"title":"Workshop","capacity":20,"target_registrations":30,"is_marketing_view":false,"auto_notify":true,"is_active":false,"is_public":true,"is_online":false}
```

`capacity` — ограничение вместимости, `target_registrations` — цель набора. Цель не увеличивает фактическую вместимость. `is_active` и `is_public` — независимые поля; настройте их под ожидаемый способ доступа и проверьте возвращённую `phase`. Начало/конец, текущие настройки цепочек и привязанные билеты видны в результате чтения.

Изменение — `PATCH /api/v1/tools/events` (`200`) с `seq_num` и изменяемыми полями. Для режима входа доступны `checkin_mode:"self_service"` и `"operator_only"`. Привязка билетов `catalog_ids` содержит **строки с tenant-local номерами каталога**; название поля не разрешает передать PK. `[]` снимает привязки. Для автоматизации передавайте возвращённые номера цепочки/шага в нужном слоте, например `registration_chain_seq_num` и `registration_chain_step_seq`; не придумывайте идентификаторы. Удаление — `POST /api/v1/tools/events/delete` с `{"seq_num":7}` (`201`).

Перед регистрацией проверьте актуальное мероприятие, вместимость, выбранный билет и клиента. Пример следующего раздела относится к уже существующим event `7`, user `13`, catalog `31` вашего тестового контекста; номера не гарантированы после создания в другом бизнесе.

## Зарегистрируйте клиента и прочитайте результат

```http
POST /api/v1/tools/event-registrations
Content-Type: application/json
Idempotency-Key: event-create-19

{"event_seq_num":7,"user_seq_num":13,"catalog_seq_num":31}
```

Полный контрактный ответ `201`:

```json
{"outcome":"payment_required","registration":{"seqNum":19,"eventSeqNum":7,"userSeqNum":13,"catalogSeqNum":31,"status":"registered","checkedInAt":null,"createdAt":"2026-09-30T10:00:00.000Z","updatedAt":"2026-09-30T11:00:00.000Z"},"replayed":false}
```

`payment_required` означает, что выбран платный билет. Эта операция регистрации сама не создаёт заказ или платёж. Для бесплатной регистрации без билета outcome — `registered`. Продолжение продажи согласуйте отдельно через [каталог и продажи](https://telegafirst.com/docs/catalog-and-sales) и [платежи](https://telegafirst.com/docs/payments): подготовка → показ сценария человеку → подтверждение → исполнение. Внутренний AI в диалоге клиента только советует и передаёт запрос оператору; собственный агент владельца использует разрешённые money tools.

Для чтения одной регистрации: `GET /api/v1/tools/event-registrations/19` (`200`). Для списка по мероприятию:

```http
GET /api/v1/tools/event-registrations?cursor=8&limit=1&event_seq_num=7
```

Полный контрактный ответ `200`:

```json
{"registrations":[{"seqNum":19,"eventSeqNum":7,"userSeqNum":13,"catalogSeqNum":31,"status":"registered","checkedInAt":null,"createdAt":"2026-09-30T10:00:00.000Z","updatedAt":"2026-09-30T11:00:00.000Z"}],"nextCursor":"19"}
```

Default `limit` — `50`, допустимо `1..100`. Следующий запрос использует `nextCursor` при том же бизнесе и фильтре мероприятия; `null` завершает список. Cursor представляет номер последовательности; `0` и значения за границей int4 отклоняются. Не переносите курсор в другой бизнес или фильтр. Эти GET не используют HTTP response receipt.

MCP-эквиваленты: `list_event_registrations`, `get_event_registration`, `create_event_registration`, `cancel_event_registration`, `check_in_event_registration`. Для одиночной операции MCP принимает `registration_seq_num`; создание — те же числовые адреса. В MCP мутация передаёт обязательный `idempotency_key` в аргументах, в REST — `Idempotency-Key` в заголовке.

## Отмена регистрации и вход на мероприятие

Отмена `POST /api/v1/tools/event-registrations/19/cancel` принимает строго пустое тело `{}` и обязательный новый `Idempotency-Key` (`201`). Для платного билета сервер возвращает `outcome:"REFUND_REQUIRED"` с прежней регистрацией: отмена не выполнена. Передайте финансовое решение человеку; возврат не следует угадывать из факта отмены. Для бесплатной регистрации успешный `outcome:"cancelled"` сопровождается `status:"cancelled"` и обновлённым `updatedAt`.

Отметка оператора: `POST /api/v1/tools/event-registrations/19/check-in`, строго `{}` и обязательный ключ. Полный контрактный ответ `201`:

```json
{"seqNum":19,"eventSeqNum":7,"userSeqNum":13,"catalogSeqNum":31,"status":"attended","checkedInAt":"2026-10-01T12:00:00.000Z","createdAt":"2026-09-30T10:00:00.000Z","updatedAt":"2026-10-01T12:00:00.000Z"}
```

Это управляющая отметка оператора с проверкой tenant и `events:write`. QR для самостоятельного входа — отдельный публичный пропуск; не заменяйте его номером регистрации. Команда работает в Telegram. `self_service` и `operator_only` регулируют выбранный сценарий входа, а номера в Hub нужны для авторизованных операций.

Три мутации регистраций используют durable квитанцию, привязанную к бизнесу, операции и ключу. Удаление HTTP-кэша не превращает повтор в новую запись. Повтор создания/отмены возвращает сохранённый результат с `replayed:true`; check-in повторяет сохранённый объект без отдельного replay-флага. Другое тело или другой номер под тем же ключом — `422 IDEMPOTENCY_KEY_MISMATCH`. Перед повтором сохраняйте исходные аргументы и проверяйте состояние. [Повторы и конфликты](https://telegafirst.com/docs/etag-and-idempotency).

## Найдите время и создайте запись

1. Прочитайте `GET /api/v1/booking/resources` (`200`) и выберите возвращённый `seqNum` ресурса. Сверьте активность, slot interval, advance/lead-time и ограничения активных записей клиента.
2. Получите доступность для услуги и календарного диапазона. `start_date`/`end_date` — дни `YYYY-MM-DD` в часовом поясе бизнеса. В конкретном слоте `startAt`/`endAt` возвращаются как абсолютные ISO timestamps.
3. Выберите слот с `bookable:true`. Результат поиска показывает доступность на момент чтения; окончательные ограничения проверяются при записи.
4. Создайте запись с нужным ресурсом либо явным `resource_seq_num:null`, если поручаете выбор серверу. Не опускайте поле вместо явного решения.

```http
GET /api/v1/booking/availability?catalog_item_seq_num=4&resource_seq_num=11&start_date=2026-10-02&end_date=2026-10-03
```

Ответ `200`:

```json
{"slots":[{"startAt":"2026-10-02T10:00:00.000Z","endAt":"2026-10-02T11:00:00.000Z","resourceSeqNum":11,"resourceName":"Room","bookable":true},{"startAt":"2026-10-02T11:00:00.000Z","endAt":"2026-10-02T12:00:00.000Z","resourceSeqNum":11,"resourceName":"Room","bookable":false}]}
```

Запрос создания (`booking:write`):

```http
POST /api/v1/booking/bookings
Content-Type: application/json
Idempotency-Key: booking-command

{"catalog_item_seq_num":4,"resource_seq_num":11,"user_seq_num":51,"start_at":"2026-10-02T10:00:00.000Z","end_at":"2026-10-02T11:00:00.000Z","quantity":2,"selection_mode":"full"}
```

Полный контрактный ответ `201`:

```json
{"seqNum":31,"status":"pending","resourceSeqNum":11,"resourceName":"Room","catalogItemSeqNum":4,"userSeqNum":51,"selectionMode":"full","startAt":"2026-10-02T10:00:00.000Z","endAt":"2026-10-02T11:00:00.000Z","durationMinutes":60,"quantity":2,"didAttend":null,"createdAt":"2026-10-01T09:00:00.000Z"}
```

`pending` — ожидающая решения запись, созданная агентом. Подтверждение остаётся за человеком-оператором; REST `/confirm` здесь не предусмотрен. Успешный поиск, создание записи и факт оплаты — разные результаты.

Варианты `selection_mode`: `full`, `time_only`, `resource_only`, `open_inquiry`. Для открытой заявки `start_at:null` и/или `resource_seq_num:null` передаются явно согласно выбранному варианту. Если указан `end_at`, длительность и множитель определяет сервер по диапазону и правилам услуги; `quantity` не заменяет эту проверку. Читайте фактические поля ответа, особенно для дневных интервалов и часового пояса бизнеса.

## Отметьте посещение или отмените запись

У записи факт визита отмечается через `POST /api/v1/booking/bookings/31/attendance` (`201`, `booking:write`, обязательный `Idempotency-Key`):

```json
{"did_attend":true}
```

В контрактном ответе запись `31` получает `status:"completed"` и `didAttend:true`; при `false` — `status:"no_show"` и `didAttend:false`. Остальные адреса, интервал и дата создания сохраняются. Нельзя прислать собственный идентификатор того, кто поставил отметку: субъект определяется авторизованным доступом. Эта отметка не является `/check-in` мероприятия; у bookings такой маршрут отсутствует.

Отмена `POST /api/v1/booking/bookings/31/cancel` принимает, например, `{"reason":"Change of plans"}` и возвращает `201` с `status:"cancelled"`. Пустое тело допустимо. Для непустого тела используйте ключ; его обязательность следует общему HTTP правилу. Недопустимый переход состояния возвращает конфликт, поэтому сверяйте результат, а не только факт HTTP-вызова. Связанные заказы/возвраты проверяются отдельным финансовым сценарием.

## Расписания и изменения ресурсов

`GET /api/v1/booking/resource-schedules` читает расписания с необязательными `resource_seq_num`, `cursor`, `limit` (`50` по умолчанию, максимум `100`). Ответ содержит `schedules` и `nextCursor`; сохраняйте бизнес и фильтр ресурса между страницами. `GET /api/v1/booking/resources/{seqNum}` и `GET /api/v1/booking/resource-schedules/{seqNum}` читают отдельную карточку.

Для создания ресурсов/расписаний используйте `POST` соответствующего collection path с обязательным ключом. Изменение ресурса — `PATCH /api/v1/booking/resources/{seqNum}`; изменение/удаление расписания — `PATCH`/`DELETE /api/v1/booking/resource-schedules/{seqNum}`. Для этих изменений нужны `Idempotency-Key` и `If-Match` текущей прочитанной версии. Например, заголовок версии из контрактной карточки:

```http
If-Match: W/"2026-09-30T11:00:00.000Z"
```

Берите актуальное значение из чтения своей карточки: поле `etag` содержит timestamp, который в примере заключён в слабый ETag заголовка. При `428 PRECONDITION_REQUIRED` добавьте `If-Match`; при `409 CONFLICT` с detail `STALE_VERSION` перечитайте объект перед новой правкой. Расписание использует `year`, `month`, `days`, минуты суток и явные интервалы перерыва; допустимые сочетания и `null` проверяются схемой/доменом. Не считайте изменение расписания подтверждением уже ожидающей записи.

## Разберите отказ до повтора

| Статус и код | Действие |
| - | - |
| `400 VALIDATION_ERROR` | Исправить типы/поля, допустимый номер, дату и строгую форму тела |
| `400 IDEMPOTENCY_KEY_REQUIRED` | Передать ключ там, где он обязателен; для регистраций пустое `{}` не отменяет это требование |
| `401 INVALID_API_KEY` | Проверить аутентификацию и не заменять credential JWT другого субъекта |
| `403 INSUFFICIENT_SCOPE` / `PUBLISHABLE_KEY_NOT_ALLOWED` / `NO_ACTIVE_BOT` | Проверить полномочия, credential, readiness и выбранный бизнес |
| `403 FORBIDDEN` в durable регистрации | Проверить, принадлежит ли реальный credential текущему бизнесу; сохранённая квитанция не обходит эту проверку |
| `404 NOT_FOUND` | Объект отсутствует или недоступен в вашем tenant; получить номер из актуального чтения |
| `409 IDEMPOTENCY_KEY_IN_FLIGHT` | Сначала проверить состояние; незавершённая durable квитанция не даёт разрешения повторить эффект с новым ключом вслепую |
| `409 IDEMPOTENCY_KEY_FAILED` | Проверить состояние и причину прежнего отказа; для нового согласованного действия нужен новый ключ |
| `409 CONFLICT` | Прочитать detail: некорректный cursor, версия, состояние записи и отсутствие credential provenance требуют разных исправлений |
| `422 IDEMPOTENCY_KEY_MISMATCH` | Повторить исходный запрос либо согласовать новый запрос с новым ключом |
| `428 PRECONDITION_REQUIRED` | Для изменения ресурса/расписания прочитать версию и передать `If-Match` |
| `413 PAYLOAD_TOO_LARGE` / `ITEM_COUNT_EXCEEDED` | Уменьшить запрос или пакет |
| `429 RATE_LIMIT_EXCEEDED` / `RATE_LIMIT_UNAVAILABLE` | Учитывать `Retry-After`, если он есть; лимитер работает с отказом при недоступности |
| `500 INTERNAL_ERROR` | Сохранить `traceId` и сначала прочитать состояние операции |

Для показанного `POST /api/v1/booking/bookings` нужны допустимый API credential и `booking:write`. Если не передать ни `X-Api-Key`, ни допустимый `Authorization: Bearer tgf_*`, проверка аутентификации завершится до создания записи. Полный контрактный отказ — HTTP `401`, `Content-Type: application/problem+json`:

```json
{
  "type": "https://telegafirst.ru/docs/errors#INVALID_API_KEY",
  "title": "Unauthorized",
  "status": 401,
  "instance": "/api/v1/booking/bookings",
  "code": "INVALID_API_KEY",
  "traceId": "0123456789abcdef0123456789abcdef",
  "detail": "API key is required (X-Api-Key or Authorization: Bearer tgf_*)",
  "hint": {
    "reason": "The credential is unknown, revoked or expired.",
    "recovery": "Issue a new credential for this account and authenticate with it."
  }
}
```

Здесь `instance` и `traceId` взяты из принятого контрактного стенда для этого конкретного пути; это не живой отказ и не идентификатор вашего обращения. Для диагностики сохраняйте фактический `traceId` своего ответа. Восстановите предусмотренную авторизацию и проверьте scope, бизнес и состояние перед повтором; ответ `401` не подтверждает успешную запись или оплату.

У HTTP-квитанций применимых booking/event shell-запросов срок `86400` секунд; durable регистрации используют собственную квитанцию и не зависят от этого срока HTTP-кэша. Возвращённые `X-Client-Slug`/`X-Bot-Username` помогают проверить бизнес, `X-RateLimit-*` — бюджет. `ETag`/`If-Match` применяются к указанным ресурсам/расписаниям; GET регистрации и ответы записи не становятся условным чтением только от наличия `If-None-Match`.

Подробные контракты: [мероприятия REST](https://telegafirst.com/docs/api-events), [регистрации REST](https://telegafirst.com/docs/api-event-registrations), [записи REST](https://telegafirst.com/docs/api-booking), [мероприятия MCP](https://telegafirst.com/docs/mcp-events), [записи MCP](https://telegafirst.com/docs/mcp-booking), [ошибки](https://telegafirst.com/docs/errors). Результаты посещения и участия можно использовать в [сегментах и цепочках](https://telegafirst.com/docs/marketing). Публичные ссылки сайта получают серверный opaque-код в соответствующей `.ru` или `.com` зоне; обе зоны предусмотрены платформой, а выбор зоны не заменяет доступ или согласие на действие.
