# Идентификаторы и страницы результатов

Source: <https://telegafirst.com/docs/identifiers-and-pagination>
Locale: ru
releaseGitSha: c9a428d902de0b479a981e43774f38b9ec077ed7
sourceContentDigest: 2d133c315def7c3004d7198f4abbe9ebd7be3db64b877cc50ea23bbb6100f8d1
Version: 1

Сохраняйте номер объекта вместе с бизнесом, которому он принадлежит. Для управления используйте номер из ответа защищённого API, для передачи публичной ссылки — выданный непрозрачный код. Номер объекта не даёт права его читать.

TelegaFirst — AI-фронт-офис в Telegram для микробизнеса. Владелец и операторы работают в Telegram; свой AI-агент клиента управляет доступным бизнесом через scoped Hub. Покупатель обращается к бизнесу через поддерживаемый канал и имеет отдельные права на свои объекты. [Авторизация](https://telegafirst.com/docs/authorization) и [scopes](https://telegafirst.com/docs/scopes-and-permissions) определяют доступ до разрешения адреса.

## Четыре вида адреса

| Поверхность                            | Адрес                                                                                   | Что разрешает доступ                                                  |
| -------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| P1: публичная ссылка                   | Непрозрачный `UniversalLink.code`; единственная читаемая строка бизнеса — `Client.slug` | Предусмотренный публичный адрес; разрешение учитывает бизнес из хоста |
| P2: кабинет покупателя                 | Номер `seq_num` внутри бизнеса                                                          | Customer session и проверка принадлежности объекта покупателю         |
| P3: управление бизнесом, CRM, Hub, MCP | Номер внутри бизнеса и название для человека                                            | Credential, доступный бизнес, scopes и актуальные полномочия          |
| P4: внутренняя база                    | Внутренний первичный ключ                                                               | Используется платформой и не передаётся через публичный периметр      |

Название поля зависит от контракта: каталог и пользователи используют `seq_id`, заказы и ответы форм — `seq_num`, записи booking — `seqNum`. Не переименовывайте их самостоятельно. Номера разных видов объектов и разных бизнесов могут совпадать. В Hub объект разрешается по бизнесу из проверенного контекста и его номеру; в кабинете покупателя дополнительно проверяется владелец объекта. Подстановка другого номера или `client_id` в тело не расширяет доступ.

В P1 код разрешается вместе с бизнесом, определённым хостом. Для карточки товара используется `/product/{code}`, для формы — `/f/{code}`. Не заменяйте код номером и не создавайте отдельные читаемые slug товара, формы или события. Зоны `.ru` и `.com` поддерживаются обе; зона определяет соответствующий рынок и платёжные требования. Показанные ниже значения — контрактные иллюстрации, а не действующие публичные ссылки.

Один товар может одновременно иметь P3 `seq_id: 7` и отдельный `deeplink_code: "ProductCode12345"`. Первое значение подходит для защищённого чтения или изменения каталога, второе — для предусмотренной публичной ссылки. Имя route-параметра `id` само по себе не означает внутренний PK: например, защищённый `/api/v1/orders/{id}` принимает номер заказа внутри бизнеса.

## Ответ формы имеет собственный номер

`form_seq_num` указывает на форму. `seq_num` указывает на конкретный ответ этой формы. `form_title` хранит название формы на момент ответа и не служит адресом. `submission_code` — отдельный публичный код существующего реестра ссылок.

Каждый успешно сохранённый ответ получает номер при создании, включая `status: "anonymous"`. Аутентифицированный Hub caller не устанавливает личность посетителя: анонимный ответ уже имеет номер, но ещё не связан с покупателем. Привязка к пользователю сохраняет номер и код. Повтор принятого запроса с его ключом также сохраняет идентичность ответа. Для старых ответов без номера перенос выделяет недостающие номера, сохраняя ранее выданные номера и публичные коды; клиенту не нужно пересоздавать объекты или заменять ссылки.

Номер ответа — положительное целое от 1 до 2147483647. P3 операции над ним используют этот номер в контексте бизнеса. Публичный код не является альтернативным P3 параметром. В контракте ответа `linked_user_seq_num` появляется при подтверждённой связи с пользователем; внутреннего `user_id` нет. Полный `payload` при чтении отсутствует по умолчанию: явный `include: "full_payload"` требует предусмотренного доступа и записывает аудит. Название и номер не отменяют защиту персональных данных.

### Пример: принять ответ формы через Hub

Результат — сохранённый анонимный ответ с собственным номером. Нужны действующий Hub credential с `push:form_submissions`, доступный бизнес, разрешённый data plane и форма №12 с декларацией, принимающей поле `email`. `<HUB_BASE_URL>` — адрес выданного вам Hub без завершающего `/`; `<API_KEY>` — локальный секрет. Значения из примера не подтверждают готовность конкретного сервера. Не вставляйте credential или реальные ответы покупателей в промпт.

```http
POST <HUB_BASE_URL>/api/v1/form-submissions HTTP/1.1
X-Api-Key: <API_KEY>
Content-Type: application/json
Idempotency-Key: form-1042

{"form_seq_num":12,"payload":{"email":"ada@example.test"}}
```

Успех, HTTP `202`:

```json
{"outcome":"accepted","seq_num":1042,"submission_code":"opaque-form-answer","status":"anonymous"}
```

Здесь форма №12 и ответ №1042 — разные P3 объекты. Код показан отдельно и не заменяет номер. При потерянном ответе повторите тот же REST-запрос с тем же ключом в его окне replay; новое тело требует нового ключа. MCP `submit_form_submission` не обещает дедупликацию двух новых вызовов: каждый новый submit создаёт новый ответ. Для P3 чтения и обработки используйте фактически доступные MCP операции форм; REST-контроллер этого семейства предоставляет POST, а выдуманные GET/detail/mark маршруты использовать нельзя. [Формы и сайт](https://telegafirst.com/docs/sites-storefront-and-forms), [повтор запросов](https://telegafirst.com/docs/etag-and-idempotency).

| Отказ                          | Действие                                                                                   |
| ------------------------------ | ------------------------------------------------------------------------------------------ |
| `401 INVALID_API_KEY`          | Проверить credential и endpoint; [авторизация](https://telegafirst.com/docs/authorization) |
| `403 INSUFFICIENT_SCOPE`       | Проверить `push:form_submissions`, актуальные права, план и платное окно                   |
| `400 IDEMPOTENCY_KEY_REQUIRED` | Передать ключ запроса                                                                      |
| `422 IDEMPOTENCY_KEY_MISMATCH` | Не менять тело принятого запроса под тем же ключом                                         |
| `409 CONFLICT`                 | Предыдущий запрос с этим ключом ещё выполняется; дождаться результата                      |
| `429 RATE_LIMIT_EXCEEDED`      | Выдержать `Retry-After`, если он указан                                                    |

Форма дополнительно проверяет объявленную schema, доступность и принадлежность бизнесу. HTTP `202` не означает, что посетитель установлен или оператор обработал ответ. Для диагностики сохраните фактический код и `traceId` без payload. [Справочник ошибок](https://telegafirst.com/docs/errors).

## Pull pagination: возвращайте cursor без изменений

Pull списки пользователей, платежей, заказов и ответов форм возвращают `success`, массив `data` и `meta` с `has_more`, `next_cursor`, `limit`; `estimated_total` может отсутствовать. Размер страницы — от 1 до 200, по умолчанию 50 для общей Pull query. Конкретная операция может задавать более узкие ограничения; используйте её schema.

Для этих четырёх Pull ресурсов cursor — зашифрованный аутентифицированный токен AES-GCM со сроком **900 секунд** от выдачи. Он связан с текущим бизнесом, видом ресурса и эффективными нормализованными фильтрами. Версия ключа находится в токене, например префикс `v1a`; это не номер страницы и не адрес объекта. Старый plaintext/base64 cursor, повреждённый токен, неизвестная версия ключа, истёкший cursor и перенос токена в другой бизнес, ресурс или фильтр дают HTTP `400 INVALID_CURSOR`. Клиенту нельзя декодировать, собирать или исправлять токен.

1. Выполните первый запрос без cursor с нужными фильтрами.
2. Обработайте `data`. Если `has_more: true`, возьмите точную строку `meta.next_cursor`.
3. Повторите запрос к тому же ресурсу и бизнесу с теми же фильтрами, передав строку cursor как URL-encoded query-параметр.
4. При `has_more: false` и `next_cursor: null` остановитесь. При `INVALID_CURSOR` начните новую выборку без старого токена; не снимайте фильтры, чтобы «починить» его.

Cursor задаёт позицию keyset-выборки, а не неизменный снимок всех данных. `estimated_total` — оценка, не условие окончания обхода. После смены активного бизнеса начинайте отдельный обход. [Выбор бизнеса](https://telegafirst.com/docs/mcp-guide).

### Пример: заказы пользователя №7

Нужны `pull:orders`, доступ к бизнесу и открытый для данных план/платное окно. API key и предусмотренный Bearer credential проходят собственную проверку. Фильтр `user_seq_num` — P3 номер пользователя, не внутренний PK. Для одного номера используется scalar; поддерживаемый IN-фильтр также принимает только положительные int4 номера. Старый `user_id` для этой выборки не принимается.

```http
GET <HUB_BASE_URL>/api/v1/orders?limit=2&user_seq_num=7 HTTP/1.1
X-Api-Key: <API_KEY>
```

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

```json
{
  "success": true,
  "data": [{
    "seq_num": 71,
    "status": "pending",
    "amount": "0",
    "currency": "RUB",
    "external_order_ref": "crm-71",
    "created_at": "2026-09-30T12:00:00.000Z",
    "updated_at": "2026-10-01T12:00:00.000Z"
  }],
  "meta": {"has_more":false,"next_cursor":null,"limit":2,"estimated_total":1}
}
```

Этот пример заканчивается одной страницей. Если ваш реальный ответ имеет следующую страницу, передайте **его** токен в `cursor`, сохранив `user_seq_num=7`. Не используйте примерный токен из другого запуска. Внешний `external_order_ref` — ссылка вашей интеграции, а не PK платформы.

Пример отказа при неверном номере пользователя, HTTP `400`, `Content-Type: application/problem+json`:

```json
{
  "type":"https://telegafirst.ru/docs/errors#INVALID_FILTER",
  "title":"Bad Request",
  "status":400,
  "instance":"/api/v1/orders",
  "code":"INVALID_FILTER",
  "traceId":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "detail":"user_seq_num must be a positive int4 number"
}
```

| Отказ Pull запроса    | Действие                                                             |
| --------------------- | -------------------------------------------------------------------- |
| `400 INVALID_CURSOR`  | Начать выборку заново с тем же бизнесом и фильтрами                  |
| `400 INVALID_FILTER`  | Исправить имя, вид или пределы фильтра                               |
| `400 INVALID_REQUEST` | Проверить размер страницы по schema операции                         |
| `401` / `403`         | Проверить credential, scopes и текущий доступ; cursor их не заменяет |

## Каталог имеет отдельный протокол страниц

`GET /api/v1/catalog` требует `catalog:read`, принимает `zone: ru|com`, `limit` от 1 до **100** с default50 и собственный cursor. Его ответ также содержит `meta.next_cursor`, но этот cursor кодирует публичный номер каталога и не имеет описанного выше encrypted900-second Pull протокола. В контрактном примере `cursor=Ng&limit=1&zone=ru` возвращает товар `seq_id:7` и `next_cursor:"Nw"`. Используйте возвращённую строку; не переносите токены между ресурсами и не принимайте её за внутренний PK. Списки событий и booking также имеют собственные schemas: универсальный формат всем спискам не навязывается.

Для чтения одного товара и его версии используйте `GET /api/v1/catalog/7?zone=ru`; после него — [ETag и идемпотентность](https://telegafirst.com/docs/etag-and-idempotency). [Каталог и продажи](https://telegafirst.com/docs/catalog-and-sales) описывает дальнейшие операции. Scopes, название credential и число в URL никогда не заменяют проверку доступного бизнеса.
