К содержанию
TelegaFirst

Для AI-агентов: markdown этой страницы — /docs/identifiers-and-pagination.mdиндекс документации — /llms.txt

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

Обновлено

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

TelegaFirst — AI-фронт-офис в Telegram для микробизнеса. Владелец и операторы работают в Telegram; свой AI-агент клиента управляет доступным бизнесом через scoped Hub. Покупатель обращается к бизнесу через поддерживаемый канал и имеет отдельные права на свои объекты. Авторизация и scopes определяют доступ до разрешения адреса.

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

ПоверхностьАдресЧто разрешает доступ
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 или реальные ответы покупателей в промпт.

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:

{"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 маршруты использовать нельзя. Формы и сайт, повтор запросов.

ОтказДействие
401 INVALID_API_KEYПроверить credential и endpoint; авторизация
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. Справочник ошибок.

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 — оценка, не условие окончания обхода. После смены активного бизнеса начинайте отдельный обход. Выбор бизнеса.

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

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

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

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

{
  "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:

{
  "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 и идемпотентность. Каталог и продажи описывает дальнейшие операции. Scopes, название credential и число в URL никогда не заменяют проверку доступного бизнеса.