Начните с ресурса, принадлежащего гостю
Baseline и ticket issuance принимают PREORDER, RESERVATION, WAITLIST и TABLE_SESSION. Сначала получите точный нативный UUID и текущую READ capability в собственном сценарии ресурса. PREORDER указывает на принятый ASAP или scheduled fulfillment. Для QR нужны выданное membership и настоящая сессия. Публичные handles, drafts, join tokens и аккаунт не дают этого READ-доступа.
Канонические исходники содержат HTTP-метод начального снимка, отдельную выдачу билета и описанный здесь протокол public-guest. Нативная проверка WebSocket ещё не завершена. Пример подготавливает данные допуска и кадр сессии; он не открывает WebSocket и не показывает событие из действующего потока.
Получите начальный снимок, затем отдельный билет
Передайте kind, resourceId и действующий приватный capability прямо в теле POST-команды начального снимка. Сервер проверяет фактический Origin браузера, фиксирует реальную позицию журнала, затем повторно проверяет права ресурса перед чтением безопасного текущего снимка. Браузерный код использует credentials: 'omit' и позволяет браузеру передать Origin. Cookie учётной записи, заголовки Authorization и ключи устройства не предоставляют запасную авторизацию.
Входные значения ниже вымышлены. Оба HTTP-ответа должны прийти от вашего действующего авторизованного сервера: код не создаёт подставные cursor, snapshot, ticket или scopeBinding. Он хранит ответы в локальной памяти, не отправляет запрос со страницы документации и подготавливает только кадр протокола, пока нативная проверка WebSocket не завершена.
- Получите UUID одного из четырёх поддерживаемых ресурсов Hallify и действующий ключ READ в пределах разрешённого Origin представления.
- Сначала прочитайте начальный снимок. Сохраните вместе snapshot, streamId public-guest, точный возвращённый cursor и scopeBinding; выбирайте сгенерированную схему ответа по snapshot.kind.
- Выдайте отдельный билет для того же ресурса и ключа. Начальный снимок не выдаёт билет или новый ключ ресурса.
- Потребуйте совпадение scopeBinding билета и начального снимка. При изменении прав остановитесь и получите новый снимок, не объединяя разные ответы.
- Подготовьте кадр сессии с тем же курсором public-guest и scopeKey из совпавшей привязки, ':' и клиентского контекста длиной от 1 до 128 символов. Для публичного перехода исходного протокола передавайте билет отдельно вместе с hallify.socket.v1.
- Храните снимок, курсор и приватный билет в памяти соответствующего сценария. Завершите нативную проверку WebSocket перед признанием исходного протокола проверенным сценарием наблюдения в реальном времени.
const resource = {
kind: 'PREORDER',
resourceId: '00000000-0000-4000-8000-000000000001',
capability: 'synthetic-read-capability-for-example-only',
};
const requestOptions = {
method: 'POST',
credentials: 'omit',
cache: 'no-store',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(resource),
} satisfies RequestInit;
const baselineResponse = await fetch(
'https://api.hallify.co/public/guest-observers/baseline',
requestOptions
);
if (!baselineResponse.ok) {
throw new Error(`HTTP ${baselineResponse.status}`);
}
const baseline = await baselineResponse.json();
const validBaseline =
baseline.streamId === 'public-guest' &&
typeof baseline.cursor === 'string' &&
baseline.cursor.length > 0 &&
typeof baseline.scopeBinding === 'string' &&
baseline.scopeBinding.length > 0 &&
baseline.snapshot?.kind === resource.kind;
if (!validBaseline) {
throw new Error('A current native observer baseline is required.');
}
const snapshot = baseline.snapshot;
const ticketResponse = await fetch(
'https://api.hallify.co/public/guest-observers/tickets',
requestOptions
);
if (!ticketResponse.ok) {
throw new Error(`HTTP ${ticketResponse.status}`);
}
const ticket = await ticketResponse.json();
if (ticket.scopeBinding !== baseline.scopeBinding) {
throw new Error('Observer authority changed; request a fresh baseline.');
}
const clientContext = 'example-panel';
const upgradeProtocols = ['hallify.socket.v1', ticket.ticket];
const sessionFrame = {
v: 1,
kind: 'session',
scopeKey: `${baseline.scopeBinding}:${clientContext}`,
streams: [baseline.streamId],
cursors: { [baseline.streamId]: baseline.cursor },
};Соблюдайте сроки допуска и права ресурса
Начальный снимок содержит безопасное текущее состояние и не заменяет бизнес-ключ ресурса. PREORDER предоставляет разрешённые статус и расписание исполнения, RESERVATION — статус бронирования, число гостей и время, WAITLIST — статус и необязательное окно принятия, TABLE_SESSION — разрешённое участие, подписи слота и эпохи доступа. Другие бизнес-данные остаются у методов соответствующего домена.
Путь перехода в исходниках — /socket/public. Передавайте ровно hallify.socket.v1 и отдельный приватный билет gpot1_; сервер выбирает только стабильный протокол. После ready кадр сессии подписывается только на public-guest и передаёт неизменённый курсор начального снимка под тем же ключом потока. cursor и scopeBinding определяют контекст ресурса и прав; сами они не предоставляют доступ. Руководство не исполняет WebSocket и не содержит примера действующего события.
База данных задаёт expiresAt билета не более чем на 30 секунд и не позднее текущих сроков ресурса или представления. Это срок одноразового допуска, а не текущего разрешения на наблюдение. Использованный или просроченный билет не разрешает ещё одно подключение. Не создавайте очередь или предварительный запас: для одного ключа допускается не более шести ожидающих билетов, а при 429 нужно соблюдать Retry-After.
Если начальный снимок возвращает 503, остановитесь без подставного курсора. При resyncRequired или изменении прав ресурса получите новый снимок; для нового перехода нужен отдельный совпадающий билет. Не переносите курсор между ресурсами и ключами. Отозванные, заменённые, удалённые, предназначенные только для повтора и неклассифицированные источники не восстанавливают права старым снимком. Билет не разрешает команды бронирования, заказа, решения сотрудника или оплаты.
Храните ключи внутри гостевого сценария
Не помещайте ключи ресурсов и билеты в URL, cookie, Redux, журналы, аналитику и содержимое ошибок. Приватные снимки и курсоры должны оставаться в контексте соответствующего ресурса. Оба HTTP-ответа используют Cache-Control: no-store. Храните курсор вместе с точными ресурсом, снимком и scopeBinding; не декодируйте, не создавайте и не исправляйте его самостоятельно. Начальный выбор представления использует x-hallify-connection-id только в методах, где он объявлен; команды наблюдения используют права, уже зафиксированные в доступе ресурса.
JOIN не является observer resource. SDK раскрывает HTTP baseline/ticket methods без вымышленного автоматического WebSocket client или polling loop. Виджет использует тот же сохранённый нативный READ-доступ; observer traffic не даёт финансовых прав. Native WebSocket, browser/live и production-квалификации отделены от HTTP helpers и source-поставки.