---
title: "Game SDK: показуйте, у що грають гравці в mssgs"
description: "Публікуйте з гри статус «Грає» з кнопкою «Приєднатися» і перевіряйте членство в спільноті. mssgs Game SDK для нативних, браузерних і мобільних ігор."
canonical: https://docs.mss.gs/uk/game-sdk
language: uk
---

# Показуйте, у що хтось грає

Дозвольте своїй грі повідомляти mssgs, що робить гравець. Друзі бачать «Грає» під його ім’ям, відкривають подробиці й натискають «Приєднатися», щоб потрапити в ту саму гру. Ваша гра також може перевіряти, чи є гравець у вашій спільноті.

## Що можна зробити

- **Публікуйте ігровий статус** Гра, що саме робить гравець, його роль і наскільки заповнена група.

- **Додайте кнопку «Приєднатися»** Друзі одним натисканням приєднуються до тієї самої гри, сервера чи лобі.

- **Перевіряйте членство** Дізнавайтеся, чи є гравець у вашій спільноті і з якими ролями.

- **Комп’ютер, браузер або телефон** Нативні ігри використовують локальний міст; браузерні й мобільні ігри працюють через ваш бекенд.

У застосунку

Статус під ім’ям і подробиці, які він відкриває. Гра опублікувала один блок JSON; решту робить застосунок.

- [Огляд](#overview)

- [Як знайти клієнт](#discover)

- [Запит дозволу](#authorize)

- [Ігровий статус](#activity)

- [Кнопка «Приєднатися»](#join)

- [Браузер і телефон](#linked)

- [Довідка](#reference)

## Огляд

Настільний застосунок mssgs запускає невеликий **локальний HTTP-міст** (bridge), з яким спілкується гра на тому самому комп’ютері. Ваша гра ніколи не звертається до наших серверів, ніколи не бачить пароля чи токена облікового запису і ніколи не може дописувати від імені гравця. Вона спілкується з копією mssgs, у яку гравець уже ввійшов, і саме ця копія вирішує, що відповісти.

Що з ним можна робити:

- Визначити, що mssgs встановлено і хтось у нього ввійшов.

- Дізнатися, хто гравець: user_guid, ім’я користувача, аватар.

- Запитати «чи є цей гравець у спільноті X?» і яку роль він там має.

- Опублікувати статус «Грає …» з кнопкою **«Приєднатися»** для інших.

- Отримати передачу приєднання, коли хтось натискає цю кнопку.

### Два шляхи

**Нативна настільна гра** спілкується з локальним мостом; саме це описують наступні розділи. Гра в **браузері або на телефоні** до цього мосту дістатися не може. Для таких ігор публікує ваш власний бекенд, для гравців, які пов’язали свій обліковий запис mssgs через QR-код або восьмисимвольний код: див. [Браузерні й мобільні ігри](#linked), де першим прикладом є [CozyCity](https://cozycity.net). Сам ігровий статус в обох випадках є тим самим блоком.

### Мінімальне розкриття за замовчуванням

Області доступу навмисно нерівноцінні. Якщо вам потрібно лише знати, «чи є ця людина в нашій спільноті», запитуйте membership.query і самі вказуйте server_guid: ви отримаєте «так» чи «ні» плюс ролі гравця там і нічого не дізнаєтеся про решту його спільнот. Повний список захищено окремою, вищою областю доступу, яку гравець має схвалити окремо.

## Як знайти клієнт

Міст слухає лише 127.0.0.1 , на першому вільному порту з невеликого діапазону. Перебирайте їх по черзі, доки один не відповість: **7440, 7441, 7442, 7443**. Збірки mssgs для розробки натомість слухають **7540–7543**, тож тестова збірка ніколи не відповідає на виклики справжньої гри.

Токен не потрібен, а відповідь нічого не каже про гравця, лише те, що mssgs тут і чи хтось увійшов.

```json
{
  "product": "mssgs",
  "api": 1,
  "client": "desktop",
  "version": "14.2.20015",
  "platform": "darwin",
  "signed_in": true,
  "scopes": ["identity", "staff", "membership.query", "servers.list", "presence.write"]
}
```

Перш ніж іти далі, перевірте product === "mssgs" та api . Якщо жоден із чотирьох портів не відповідає, mssgs не запущено. Просто запропонуйте звичайний досвід гри, а не змушуйте гравця чекати.

## Запит дозволу

Усе, крім /hello , потребує токена, а токен з’являється лише після того, як гравець схвалив вашу гру в діалозі всередині застосунку. Запитуйте лише ті області доступу, якими справді користуєтеся: гравець бачить кожну окремо, з поясненням, і може зняти позначку з будь-якої.

```bash
curl -X POST http://127.0.0.1:7440/mssgs/v1/authorize \
  -H "Content-Type: application/json" \
  -d '{
    "game_id": "com.acme.spacegame",
    "name": "Space Raiders",
    "scopes": ["identity", "membership.query", "presence.write"]
  }'
```

Ви отримуєте {"status":"pending","request_id":"…","poll_after_ms":1000} , а гравець бачить діалог. Далі опитуйте, доки він не відповість (запит спливає через 3 хвилини):

```bash
curl http://127.0.0.1:7440/mssgs/v1/authorize/<request_id>

# {"status":"approved","token":"…","scopes":["identity","presence.write"],"game_id":"com.acme.spacegame"}
```

### Завжди перевіряйте, що ви насправді отримали

scopes у відповіді можуть бути **коротшими**, ніж ви запитували: гравець може зняти позначку з окремих областей. У прикладі вище membership.query було відхилено. Розгалужуйте логіку за тим, що каже відповідь, а не за тим, що ви запитували, інакше натрапите на 403 MISSING_SCOPE , якого не планували.

Збережіть токен і надсилайте його як Authorization: Bearer <token> . Він переживає перезапуски, тож гравець схвалює вашу гру один раз, а не на кожну сесію. Якщо згодом ви повторно авторизуєтеся з уже наданими областями доступу, ви одразу отримаєте той самий токен без діалогу.

## Області доступу й приватність

П’ять областей доступу розкривають дуже різні обсяги даних. Це не випадковість, а вся суть задуму. Запитуйте якомога менше, рухаючись цією таблицею згори вниз.

| Область доступу | Що дозволяє | Що віддає гравець |
| --- | --- | --- |
| presence.write | **Показувати, у що грає гравець** | Нічого. Ця область доступу лише записує; вона взагалі не читає даних облікового запису. |
| identity | **Хто гравець** | user_guid, ім’я користувача, відображуване ім’я, URL аватара. |
| staff | **Позначки персоналу та модератора** | Два булеві значення понад identity. Окремо, бо гра, що показує ім’я, не має жодних підстав знати, що гравець модерує спільноти. |
| membership.query | **Перевірити спільноту, яку ви вже знаєте** | Для вказаного вами server_guid: так чи ні, його назва і ролі, які гравець там має. Нічого про жодну іншу спільноту. |
| servers.list | **Усі спільноти гравця** | Повний список: guid, назви, значки й ролі. Це найдорожча область доступу: запитуйте її, лише якщо вона справді потрібна. |

### Більшості ігор потрібні дві з них

identity і presence.write покривають «хто ви» і «показати, у що ви граєте», а це майже кожна інтеграція. Додайте membership.query , якщо хочете прив’язати винагороду до членства у вашій спільноті. servers.list вам майже ніколи не потрібна, і гравець бачить її виділеною червоним.

## Перевірка членства

Це альтернатива запиту «дайте мені весь список». Ви вказуєте server_guid власної спільноти (який ви й так знаєте) і отримуєте відповідь лише про неї.

```bash
curl -H "Authorization: Bearer $TOKEN" \
  "http://127.0.0.1:7440/mssgs/v1/membership?server_guid=ca94ecc…f4g02"

# учасник:
# {"server_guid":"ca94ecc…","member":true,"name":"Acme Fans","is_owner":false,
#  "roles":[{"guid":"0aa32…","name":"Pro"}]}

# не учасник, і більше нічого:
# {"server_guid":"…","member":false}
```

«Ні» означає саме це і нічого більше. За один виклик можна передати до 10 guid (повторіть server_guid або перелічіть через кому), і тоді повертається масив results . Група @everyone ніколи не потрапляє в roles : вона стосується кожного учасника, тож нічого вам не каже.

## Публікація ігрового статусу

Один PUT додає рядок «Грає …» під ім’ям гравця всюди, де його бачать його спільноти.

```json
{
  "name":    "Space Raiders",
  "details": "Sector 7",
  "state":   "In a raid",
  "role":    "Gunner",
  "started_at": 1755859200000,
  "party":   { "size": 3, "max": 4, "kind": "party" },
  "join":    { "secret": "raid-42" }
}
```

Обов’язкове лише поле name . Відповідь каже, скільки живе статус і як часто надсилати heartbeat:

```json
{ "ok": true, "expires_in_ms": 90000, "heartbeat_every_ms": 30000 }
```

### Надсилайте heartbeat, інакше статус зникне

Статус без ознак життя протягом 90 секунд прибирається автоматично. Так задумано: якщо ваша гра аварійно завершиться, гравець не залишиться «у грі» годинами. Надсилайте POST /mssgs/v1/activity/heartbeat кожні 30 секунд і DELETE /mssgs/v1/activity під час коректного завершення.

### Кількість гравців і роль

party.kind визначає, яке речення буде показано, бо ті самі два числа означають різне. Загін із чотирьох це не сервер, на якому четверо гравців.

| kind | Показується як | Для |
| --- | --- | --- |
| party (типово) | 3 з 4 у групі | загін, команда чи група |
| server | 4/100 гравців | ігровий сервер (FiveM, сервер спільноти) |
| lobby | 4/100 гравців | лобі до початку матчу |
| match | 4/100 гравців | матч або раунд, що триває |

role (до 48 символів) означає, *ким* грає гравець: професія, клас чи персонаж. Це окреме поле, а не ще одне речення в state , бо воно показується як мітка поруч із кількістю гравців.

details і state обмежено 128 символами кожне, name 64 символами. Розриви рядків і керівні символи вилучаються. **URL значка навмисно не підтримується**: його завантажував би кожен клієнт, що показує рядок, і статус перетворився б на маячок, який повідомляє вашому серверу про кожного учасника кожної спільноти, де є гравець.

## Кнопка «Приєднатися»

Додайте до своєї активності блок join , і інші учасники побачать поруч зі статусом кнопку **«Приєднатися»**. Є два способи, і їх можна поєднувати.

### 1. Секрет (для нативних ігор)

Задайте {"join":{"secret":"raid-42"}} . Коли хтось натискає «Приєднатися», цей секрет доставляється в *його власну* копію вашої гри, на його власному комп’ютері, за тим самим game_id . Жоден URL не відкривається і жоден обробник схеми не викликається. Ваша гра забирає його так:

```json
{
  "events": [
    { "seq": 1, "type": "join", "secret": "raid-42",
      "from": { "user_guid": "62e377…", "username": "mssgs-test-1" } }
  ],
  "cursor": 1
}
```

Опитуйте з ?since=<cursor> , щоб бачити кожну подію один раз. Якщо гра того, хто натиснув, не запущена, нічого не доставляється, і це вагома причина також запропонувати URL.

### 2. URL https (для вебігор і посилань на лобі)

Задайте {"join":{"url":"https://play.example.com/s/abc"}} , і кнопка відкриє це посилання. **Приймається лише https .** Власна схема ( steam:// , mygame:// , file:// ) відхиляється: цей блок з’являється на екрані кожного учасника, а такий URL дає змогу змусити чужий комп’ютер викликати локальний обробник з аргументами, які вибрали ви.

### Усе в join є публічним

Блок join транслюється кожному, хто бачить статус гравця; у цьому й полягає суть кнопки «Приєднатися». Тож ставтеся до нього як до коду лобі, а не як до облікових даних. Ніколи не кладіть туди нічого, що має лишатися таємним, і обмежуйте строк дії своїх кодів.

## FiveM

У клієнтському Lua-середовищі FiveM немає HTTP, тож ресурс спілкується з мостом через **NUI**, представлення CEF, яке надсилає Origin. Міст явно приймає такі origin: https://cfx-nui-<resource> і старіший nui://<resource> . Звичайні вебсторінки й далі відхиляються, а сторінка з відкритого інтернету не може заявити такий origin: браузер встановлює його сам.

```lua
-- HTTP-запити робить сторінка NUI; Lua лише передає їй дані.
CreateThread(function()
  while true do
    SendNUIMessage({
      action  = 'mssgs:publish',
      players = GetActivePlayers and #GetActivePlayers() or 0,
      maxPlayers = GetConvarInt('sv_maxclients', 100),
      job     = exports['qb-core'] and 'Police' or nil
    })
    Wait(30000) -- heartbeat: статус спливає через 90 с
  end
end)
```

```javascript
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // пробуємо 7440-7443
let token = null;

async function authorize () {
  const res = await fetch(`${BASE}/authorize`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      game_id: 'fivem.lossantos.rp',
      name: 'Los Santos Roleplay',
      scopes: ['presence.write']          // більше тут нічого не потрібно
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Тепер гравець бачить діалог дозволу в mssgs.
  for (let i = 0; i < 180; i += 1) {
    await new Promise((r) => { setTimeout(r, 1000); });
    const poll = await (await fetch(`${BASE}/authorize/${started.request_id}`)).json();
    if (poll.status === 'approved') { return poll.token; }
    if (poll.status !== 'pending') { return null; }
  }
  return null;
}

window.addEventListener('message', async (event) => {
  if (event.data.action !== 'mssgs:publish') { return; }
  if (!token) { token = await authorize(); }
  if (!token) { return; }

  await fetch(`${BASE}/activity`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
    body: JSON.stringify({
      name: 'FiveM',
      details: 'Los Santos Roleplay',
      role: event.data.job,                       // "Police"
      party: { size: event.data.players, max: event.data.maxPlayers, kind: 'server' },
      join: { url: 'https://cfx.re/join/abc123' } // ваше посилання cfx.re
    })
  });
});
```

Результат: **Грає FiveM · Los Santos Roleplay · 4/100 гравців · Police**, з кнопкою «Приєднатися», що відкриває ваше посилання cfx.re.

### Запитуйте лише presence.write

Ігровому статусу більше нічого не потрібно: ця область доступу взагалі нічого не читає. Якщо хочете прив’язати внутрішньоігрову винагороду до членства у вашій спільноті mssgs, додайте membership.query і вкажіть власний server_guid; ви однаково нічого не дізнаєтеся про інші спільноти гравця.

### Сервер, на якому ви граєте, не є автоматично довіреним

Будь-який сервер FiveM може запускати клієнтські ресурси, тож будь-який сервер, до якого хтось приєднується, може запитати дозвіл. Саме тому між ними стоїть діалог, що називає ресурс: вирішує гравець, а не сервер.

## Браузерні й мобільні ігри: зв’язок через ваш бекенд

Гра у вкладці браузера або на телефоні не може дістатися до описаного вище мосту. Він працює на комп’ютері гравця, і між ними стоять три стіни: міст відхиляє кожен запит із браузерним Origin , Chrome показує запит дозволу, коли публічна сторінка звертається до 127.0.0.1 , а Safari одразу відмовляє, і телефон узагалі не має шляху до loopback-інтерфейсу комп’ютера.

Тому напрям змінюється. **Ваш власний бекенд уже знає, хто грає, і повідомляє про це mssgs**, для гравців, які пов’язали свій обліковий запис mssgs з вашою грою. Зв’язок схвалюється *у застосунку mssgs*, ніколи не у вашій грі, і він створює зв’язок, а не сесію: ніщо з описаного нижче не може нікого авторизувати чи діяти від імені гравця. Клієнт вашої гри ніколи не бачить ключа і ніколи не звертається до mss.gs. Перша гра на цьому шляху це [CozyCity](https://cozycity.net), симулятор будівництва міста, що постачається як WebGL-сторінка та застосунок для iPhone без настільної збірки; приклади нижче взято з неї.

### 1. Зареєструйте свою гру

Зареєструйте гру на [сторінці реєстрації Game SDK](https://mss.gs/uk/docs/game-sdk/register): свій game_id (наприклад, com.deverence.cozycity ), назву й значок, які гравець бачить на аркуші схвалення, і хости свого бекенда. Ми перевіримо її там, і після схвалення ваш **ключ бекенда** чекатиме на тій самій сторінці, показаний один раз; ми зберігаємо лише його дайджест. Ключ має бути на вашому сервері й більше ніде. Там само його можна замінити будь-коли, а старий залишається дійсним 24 години, щоб деплой устиг пройти.

[Зареєструвати гру](https://mss.gs/uk/docs/game-sdk/register)

Назва й значок на аркуші **завжди беруться з реєстрації**, ніколи із запиту. Інакше фішингове посилання могло б видати запит на зв’язок за будь-яку гру. Хости визначають, куди може вказувати join.url , див. нижче.

### 2. Пов’яжіть гравця

Гравець вибирає у вашій грі **«Під’єднати mssgs»**. Ваша гра звертається до вашого бекенда, а ваш бекенд до нас:

```bash
curl -X POST https://ams1-gateway.mss.gs/game-sdk/v1/link/start \
  -H "Authorization: Bearer $BACKEND_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "player_ref": "player-8812", "player_name": "René\u2019s city" }'

# {"link_code":"K7PQ2XM4","device_code":"…","qr_url":"https://mss.gs/gl/K7PQ2XM4",
#  "deep_link":"mssgs://link-game/K7PQ2XM4","expires_in":600,"interval":5}
```

player_ref це ваш власний стабільний ID гравця (до 128 символів), а не сесії чи матчу; player_name (до 64) аркуш показує як «Гравець: …». Передавайте клієнту гри лише link_code , qr_url і deep_link . device_code це ваш дескриптор для опитування, і він лишається на сервері.

Далі ваша гра показує одразу три речі, бо гравець може бути будь-де:

- **QR-код** з qr_url . Телефон із mssgs відкриває його просто на аркуші схвалення в застосунку. Без застосунку він потрапляє на сторінку mss.gs, яка показує код і пропонує завантаження.

- **Кнопку «Відкрити в mssgs»** з deep_link , для настільного браузера поруч із настільним застосунком. Це єдиний зовнішній URL, який вашій грі будь-коли знадобиться відкрити.

- **Сам код**, двома групами по чотири символи, щоб ввести його в **Налаштування → Ігрова активність → Пов’язати гру**. В алфавіті немає 0/O та 1/I, тож помилки під час введення трапляються рідко.

Що гравець бачить у mssgs; застосунок малює це з реєстрації:

### Під’єднати CozyCity до вашого облікового запису mssgs?

CozyCity зможе показувати, у що ви граєте, як ваш статус mssgs. Гра не бачитиме ваших повідомлень, друзів чи серверів і не зможе дописувати від вашого імені. Гравець: *René’s city* · **Під’єднати** / **Не зараз**

Тим часом ваш бекенд опитує кожні interval секунд (на частіші запити приходить 429 SLOW_DOWN ), доки статус не зміниться. Код працює один раз і спливає через десять хвилин:

```bash
curl -X POST https://ams1-gateway.mss.gs/game-sdk/v1/link/poll \
  -H "Authorization: Bearer $BACKEND_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "device_code": "…" }'

# {"status":"pending"}
# {"status":"denied"}      # гравець вибрав «Не зараз»
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}
```

Збережіть link_guid для свого гравця; відтепер це адреса, для якої ви публікуєте. Відповідь містить лише user_guid ; username додається, тільки якщо ваша реєстрація має область доступу identity.link , і нічого понад це. Повторне схвалення для того самого player_ref **замінює** попередній зв’язок, тож один гравець вашої гри це один обліковий запис mssgs. Той самий обліковий запис mssgs може бути пов’язаний із кількома іграми та з кількома player_ref однієї гри (сімейний iPad).

### 3. Опублікуйте ігровий статус

Той самий блок, що й для [мосту](#activity), з тими самими правилами й обмеженнями, лише тепер для кожного зв’язку окремо та з вашим ключем бекенда:

```json
{
  "activity": {
    "name":       "CozyCity",
    "details":    "Lantern Hollow",
    "state":      "Day 12 · 34 residents",
    "started_at": 1788901000000,
    "party":      { "size": 6, "max": 40, "kind": "server" },
    "join":       { "url": "https://cozycity.net/game/?share=…" }
  }
}
```

```bash
200 {"published":true,"changed":true}     # блок змінився і його розіслано
200 {"published":true,"changed":false}    # збігається зі збереженим; оновлено лише TTL
204                                        # збережено, але гравець зараз не в мережі в mssgs
410 {"error":"LINK_REVOKED"}               # гравець від’єднався: видаліть зв’язок
```

Обробляйте 200 і 204 однаково: збережено. { "activity": null } прибирає блок; надсилайте його, коли гравець виходить. Одна відмінність від мосту: хост join.url має бути одним із ваших зареєстрованих бекендів (або його піддоменом), інакше ви отримаєте 400 INVALID_PAYLOAD . Тож бекенд не може додати до статусу гравця кнопку «Приєднатися», що веде туди, де цей гравець ніколи не грав.

### Heartbeat кожні 60 секунд, TTL 120

Опублікований статус живе **120 секунд** без нового повідомлення, а потім зникає сам. Тож надсилайте той самий блок повторно кожні 60 секунд; незмінений блок нічого не коштує й лише оновлює TTL. Якщо ваш heartbeat припиняється, зникає й рядок «Грає …», і саме в цьому суть.

Коли в мережі сотні гравців, надсилайте heartbeat одним викликом, до 100 елементів за раз. Кожен елемент отримує власний статус, тож один гравець, що від’єднався в mssgs, ніколи не зупиняє решту дев’яносто дев’ять:

```json
{ "items": [ { "link_guid": "…", "activity": { "name": "CozyCity", "details": "Lantern Hollow" } },
             { "link_guid": "…", "activity": null } ] }

// → 200 { "results": [ { "link_guid": "…", "status": 200, "changed": false },
//                      { "link_guid": "…", "status": 410 } ] }
```

### Як показується статус

- Так само, як статус із мосту: **Грає CozyCity · Lantern Hollow · 6/40 гравців**, з кнопкою «Приєднатися», коли є join.url . На сервері блок позначається як via: "backend" , тож клієнт може додати «Надіслано сервером гри».

- **Лише поки гравець у мережі в mssgs.** Якщо жоден клієнт mssgs не відкрито, обліковий запис не в мережі й таким лишається; ваш бекенд не може створити враження, що хтось присутній. Це також не дає цьому шляху стати маячком «чи Рене зараз за комп’ютером».

- Пріоритет: **гра в застосунку > гра через міст > ваш бекенд**. Якщо гравець сідає грати в шахи всередині mssgs, поки ваш бекенд і далі надсилає heartbeat, перемагають шахи, а не той, хто писав останнім.

### Від’єднання

Гравець бачить кожен зв’язок у **Налаштування → Ігрова активність → Пов’язані ігри**: з вашим значком і назвою, ім’ям гравця з вашої гри, датою зв’язування й останньої публікації та кнопкою **«Від’єднати»**. Після цього ваша наступна публікація отримує відповідь 410 LINK_REVOKED ; так ваша гра про це дізнається. Видаліть link_guid і знову запропонуйте «Під’єднати mssgs». Зі свого боку ви завершуєте зв’язок через DELETE /game-sdk/v1/links/{link_guid} .

### Обмеження

Для одного зв’язку *зміна* зараховується щонайбільше раз на 2 секунди; незмінений heartbeat безкоштовний. На один ключ припадає 600 запитів на хвилину, причому елементи пакета рахуються окремо: heartbeat для 300 гравців кожні 60 секунд витрачає 5 із 600.

## Довідник ендпоінтів: міст

Базовий URL http://127.0.0.1:<port> . Усе, крім перших трьох, потребує Authorization: Bearer <token> .

| Метод | Шлях | Scope | Що він робить |
| --- | --- | --- | --- |
| GET | /mssgs/v1/hello | немає | Чи є тут mssgs, яку версію API він підтримує і чи хтось увійшов. Єдиний маршрут, якому не потрібен токен, і він нічого не каже про гравця. |
| POST | /mssgs/v1/authorize | немає | Запитує в гравця дозвіл. Відкриває діалог у застосунку й повертає request_id для опитування. |
| GET | /mssgs/v1/authorize/:request_id | немає | pending, approved (з токеном), denied або expired. |
| GET | /mssgs/v1/me | identity | Гравець, який увійшов. Додає is_staff / is_moderator лише з областю доступу staff. |
| GET | /mssgs/v1/membership | membership.query | Членство в спільнотах із переданими значеннями server_guid (до 10, повторено або через кому). |
| GET | /mssgs/v1/servers | servers.list | Усі спільноти гравця з його ролями. Особисті повідомлення ніколи не включаються. |
| PUT | /mssgs/v1/activity | presence.write | Публікує блок «Грає …». Повертає TTL і те, як часто надсилати heartbeat. |
| POST | /mssgs/v1/activity/heartbeat | presence.write | Підтримує опубліковану активність, не надсилаючи її повторно. |
| DELETE | /mssgs/v1/activity | presence.write | Негайно прибирає її, для коректного завершення. |
| GET | /mssgs/v1/events | presence.write | Передачі приєднання, адресовані вашій грі. Опитуйте з ?since=<cursor>. |
| GET | /mssgs/v1/session | немає | Що містить цей токен: game_id, надані області доступу, чи хтось увійшов. |
| DELETE | /mssgs/v1/session | немає | Повертає дозвіл. Той самий ефект, що й відкликання гравцем у Налаштуваннях. |

## Довідник ендпоінтів: пов’язані бекенди

Базовий URL https://ams1-gateway.mss.gs . Кожен маршрут потребує Authorization: Bearer <backend key> і області доступу activity.write у вашій реєстрації; відповіді надсилаються з Cache-Control: no-store . Викликайте їх зі свого сервера, ніколи з клієнта гри.

| Метод | Шлях | Що він робить |
| --- | --- | --- |
| POST | /game-sdk/v1/link/start | Починає зв’язок для одного з ваших гравців ({ player_ref, player_name? }). Повертає link_code, device_code, qr_url, deep_link, expires_in та interval. |
| POST | /game-sdk/v1/link/poll | { device_code } → pending, denied, expired або linked з link_guid і user. |
| DELETE | /game-sdk/v1/links/{link_guid} | Завершує зв’язок з вашого боку. Гравець може зробити те саме в Налаштуваннях. |
| PUT | /game-sdk/v1/links/{link_guid}/activity | Публікує блок «Грає …» для одного гравця; { "activity": null } прибирає його. |
| POST | /game-sdk/v1/activity/batch | Те саме для до 100 гравців одним викликом. Кожен елемент відповідає окремо. |

## Коди помилок

Помилки повертаються як {"error":"CODE","message":"…"} з відповідним HTTP-статусом.

| Код | Значення |
| --- | --- |
| 401 UNAUTHORIZED | Токен відсутній або невідомий; спершу авторизуйтеся. |
| 403 MISSING_SCOPE | Гравець не надав цей дозвіл. Можливо, він зняв з нього позначку. |
| 403 ORIGIN_NOT_ALLOWED | Запит містив браузерний Origin. Див. «Лише нативні ігри» нижче. |
| 409 NOT_SIGNED_IN | mssgs запущено, але ніхто не ввійшов. |
| 429 RATE_LIMITED | Понад 120 запитів на хвилину від однієї гри. |
| 400 INVALID_GAME_ID | game_id може містити лише літери, цифри, крапку, дефіс або підкреслення. |
| 400 TOO_MANY_GUIDS | Щонайбільше 10 значень server_guid за один виклик membership. |

### Пов’язані бекенди

Маршрути бекенда використовують ту саму форму. У пакеті статус повертається для кожного елемента окремо в results , тож один завершений зв’язок ніколи не зриває весь виклик.

| Код | Значення |
| --- | --- |
| 401 INVALID_BACKEND_KEY | Невідомий ключ або ключ, ротований понад 24 години тому. |
| 403 SCOPE_NOT_GRANTED | Ваша реєстрація не має області доступу, потрібної цьому маршруту. |
| 400 INVALID_PAYLOAD | Некоректне тіло запиту, понад 100 елементів у пакеті або join.url, хост якого не є одним із ваших зареєстрованих бекендів. |
| 400 INVALID_ACTIVITY | Після нормалізації не лишилося придатного name. |
| 410 LINK_REVOKED | Зв’язок завершено з того чи іншого боку. Видаліть його й знову запропонуйте «Під’єднати mssgs». |
| 429 SLOW_DOWN | Ви опитували link/poll частіше, ніж дозволяє interval. |
| 429 RATE_LIMITED | Зміна одного зв’язку менш ніж через 2 секунди після попередньої або понад 600 запитів на хвилину з вашим ключем. |
| 503 LINK_STORE_UNAVAILABLE | Тимчасова проблема на нашому боці. Повторіть під час наступного heartbeat. |

## Безпека

### Лише нативні ігри

Запити з Origin вебсторінки відхиляються з 403 ORIGIN_NOT_ALLOWED . Якби будь-яка вебсторінка могла визначити, що у вас запущено mssgs, і викликати діалог дозволу, це була б поверхня для фінгерпринтингу й фішингу, а не функція. Нативна гра взагалі не надсилає Origin, тож її це не стосується, а вбудований браузер самої гри дозволено за назвою, див. [FiveM](#fivem). Якщо ви створюєте браузерну чи мобільну гру, ви не звертаєтеся до мосту: ваш власний бекенд публікує для пов’язаних гравців, див. [Браузерні й мобільні ігри](#linked).

### Що лишається під контролем гравця

- Гравець може вимкнути міст у **Налаштування → Ігрова активність**, і після цього жодна гра взагалі не бачитиме mssgs.

- Кожна схвалена гра вказана там разом із точними дозволами, які вона має, часом останньої активності та кнопкою **«Прибрати»**. Прибирання діє негайно: токен одразу перестає працювати.

- Пов’язана браузерна чи мобільна гра вказана в **Пов’язані ігри** з кнопкою **«Від’єднати»**. Від’єднання теж діє негайно: наступна публікація цього бекенда отримує 410 .

- Міст слухає лише 127.0.0.1, ніколи мережу.

- Особисті повідомлення ніколи не розкриваються, навіть із servers.list.

- Для кожної гри діє ліміт 120 запитів на хвилину.

### Як бути добрим сусідом

- Запитуйте області доступу тоді, коли вони потрібні, а не всі одразу під час першого запуску.

- Працюйте й без mssgs: гравець не зобов’язаний його мати.

- Прибирайте свій статус, коли гра припиняється, а не чекайте на TTL.

- Сприймайте відхилену область доступу як звичайний результат, а не як помилку.

## Що далі
