---
title: "Game SDK: mostra a cosa giocano i giocatori su mssgs"
description: "Pubblica uno stato “Sta giocando a” con un pulsante Unisciti ora e verifica l’appartenenza alla community. Il Game SDK di mssgs per giochi nativi, web e mobile."
canonical: https://docs.mss.gs/it/game-sdk
language: it
---

# Mostra a cosa sta giocando qualcuno

Fai dire al tuo gioco a mssgs cosa sta facendo un giocatore. Gli amici vedono “Sta giocando a” sotto il suo nome, aprono i dettagli e premono Unisciti ora per entrare nella stessa partita. Il tuo gioco può anche verificare se un giocatore fa parte della tua community.

## Cosa puoi fare

- **Pubblica uno stato di gioco** Il gioco, cosa sta facendo il giocatore, il suo ruolo e quanto è pieno il gruppo.

- **Aggiungi un pulsante Unisciti ora** Gli amici entrano nella stessa partita, server o lobby con una sola pressione.

- **Verifica l’appartenenza** Chiedi se un giocatore fa parte della tua community, e con quali ruoli.

- **Desktop, browser o telefono** I giochi nativi usano il bridge locale; i giochi browser e per telefono passano dal tuo backend.

Nell'app

Lo stato sotto un nome, e i dettagli che apre. Il gioco ha pubblicato un solo blocco JSON; il resto è l’app.

- [Panoramica](#overview)

- [Trovare il client](#discover)

- [Chiedere il permesso](#authorize)

- [Stato di gioco](#activity)

- [Unisciti ora](#join)

- [Browser e telefono](#linked)

- [Riferimento](#reference)

## Panoramica

L’app desktop di mssgs esegue un piccolo **bridge HTTP locale** con cui dialoga un gioco sulla stessa macchina. Il tuo gioco non parla mai con i nostri server, non vede mai la password o un token dell’account e non può mai pubblicare al posto del giocatore. Parla con la copia di mssgs in cui il giocatore ha già fatto l’accesso, ed è quella copia a decidere cosa rispondere.

Cosa puoi farci:

- Rilevare che mssgs è installato e che qualcuno ha fatto l’accesso.

- Leggere chi è il giocatore: user_guid, nome utente, avatar.

- Chiedere “questo giocatore fa parte della community X?” e quale ruolo ha lì.

- Pubblicare uno stato “Sta giocando a …” con un pulsante **Unisciti ora** per gli altri.

- Ricevere il passaggio di un ingresso quando qualcuno preme quel pulsante.

### Due modi di entrare

Un **gioco desktop nativo** dialoga con il bridge locale; è ciò che descrivono le sezioni seguenti. Un gioco **nel browser o su un telefono** non può raggiungere quel bridge. In quel caso è il tuo backend a pubblicare, per i giocatori che hanno collegato il proprio account mssgs tramite un QR o un codice di otto caratteri: vedi [Giochi browser e per telefono](#linked), con [CozyCity](https://cozycity.net) come primo esempio. Lo stato di gioco in sé è lo stesso blocco in entrambi i casi.

### Divulgazione minima, per impostazione predefinita

Gli scope sono volutamente diseguali. Se tutto ciò che ti serve è “questa persona fa parte della nostra community”, chiedi membership.query e indichi tu il server_guid: ottieni sì/no più i suoi ruoli lì, e non vieni a sapere nulla delle sue altre community. L’elenco completo sta dietro uno scope separato e più alto, che il giocatore deve approvare a parte.

## Trovare il client

Il bridge è in ascolto solo su 127.0.0.1 , sulla prima porta libera di un piccolo intervallo. Provale in ordine finché una risponde: **7440, 7441, 7442, 7443**. Le build di sviluppo di mssgs sono invece in ascolto su **7540–7543**, così una build di test non risponde mai alle chiamate di un gioco vero.

Non serve un token, e la risposta non dice nulla sul giocatore, solo che mssgs è qui e se qualcuno ha fatto l’accesso.

```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"]
}
```

Controlla product === "mssgs" e api prima di andare avanti. Se nessuna delle quattro porte risponde, mssgs non è in esecuzione. Offri semplicemente la tua esperienza normale invece di far aspettare il giocatore.

## Chiedere il permesso

Tutto tranne /hello richiede un token, e un token esiste solo dopo che il giocatore ha approvato il tuo gioco in una finestra di dialogo dentro l’app. Chiedi solo gli scope che usi davvero: il giocatore li vede uno per uno, spiegati, e può deselezionarli singolarmente.

```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"]
  }'
```

Ricevi {"status":"pending","request_id":"…","poll_after_ms":1000} e il giocatore vede la finestra di dialogo. Poi interroga finché non risponde (la richiesta scade dopo 3 minuti):

```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"}
```

### Controlla sempre cosa hai ottenuto davvero

Gli scopes nella risposta possono essere **meno** di quelli che hai chiesto: il giocatore è libero di deselezionarne alcuni. Nell’esempio qui sopra, membership.query è stato rifiutato. Decidi in base a cosa dice la risposta, non a cosa hai richiesto, altrimenti incontrerai un 403 MISSING_SCOPE che non avevi previsto.

Salva il token e invialo come Authorization: Bearer <token> . Sopravvive ai riavvii, quindi il giocatore approva il tuo gioco una volta sola e non a ogni sessione. Se in seguito ripeti l’autorizzazione con scope già concessi, ricevi subito lo stesso token, senza finestra di dialogo.

## Scope e privacy

I cinque scope cedono quantità molto diverse di informazioni. Non è un caso; è l’intero progetto. Chiedi il meno possibile, scendendo lungo questa tabella.

| Scope | Cosa permette | Cosa cede il giocatore |
| --- | --- | --- |
| presence.write | **Mostrare a cosa sta giocando** | Niente. Questo scope scrive soltanto; non legge alcun dato dell’account. |
| identity | **Chi è il giocatore** | user_guid, nome utente, nome visualizzato, URL dell’avatar. |
| staff | **Flag di staff / moderatore** | Due booleani, in aggiunta a identity. Separato perché un gioco che mostra un nome non ha motivo di sapere che il giocatore modera delle community. |
| membership.query | **Verificare una community che conosci già** | Per un server_guid che indichi tu: sì/no, il suo nome e i ruoli che quel giocatore ha lì. Nulla su nessun’altra community. |
| servers.list | **Tutte le community di cui fa parte** | L’elenco completo: guid, nomi, icone e ruoli. È quello costoso: chiedilo solo se ti serve davvero. |

### Alla maggior parte dei giochi ne servono due

identity e presence.write coprono “chi sei” e “mostra a cosa stai giocando”, cioè quasi ogni integrazione. Aggiungi membership.query se vuoi legare una ricompensa all’appartenenza alla tua community. Non ti serve quasi mai servers.list , e il giocatore lo vede evidenziato in rosso.

## Verificare l’appartenenza

È l’alternativa a “dammi l’elenco completo”. Indichi il server_guid della tua community (che conosci già) e ricevi una risposta solo su quella.

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

# un membro:
# {"server_guid":"ca94ecc…","member":true,"name":"Acme Fans","is_owner":false,
#  "roles":[{"guid":"0aa32…","name":"Pro"}]}

# non è membro, e nient’altro:
# {"server_guid":"…","member":false}
```

Un “no” è esattamente questo e niente di più. Puoi passare fino a 10 guid per chiamata (ripeti server_guid o separali con virgole), e ricevi un array results . Il gruppo @everyone non è mai in roles : vale per ogni membro, quindi non ti dice nulla.

## Pubblicare uno stato di gioco

Un solo PUT mette la riga “Sta giocando a …” sotto il nome del giocatore, ovunque le sue community lo vedano.

```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" }
}
```

Solo name è obbligatorio. La risposta ti dice quanto dura lo stato e ogni quanto inviare l’heartbeat:

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

### Heartbeat, o lo stato sparisce

Uno stato senza segni di vita per 90 secondi viene cancellato automaticamente. È voluto: se il tuo gioco va in crash, il giocatore non resta “in gioco” per ore. Invia un POST /mssgs/v1/activity/heartbeat ogni 30 secondi, e DELETE /mssgs/v1/activity alla chiusura regolare.

### Numero di giocatori e ruolo

party.kind decide quale frase viene mostrata, perché gli stessi due numeri non significano la stessa cosa. Una squadra di quattro non è un server con quattro giocatori.

| kind | Appare come | Per |
| --- | --- | --- |
| party (predefinito) | 3 di 4 nel gruppo | una squadra, una crew o un gruppo |
| server | 4/100 giocatori | un server di gioco (FiveM, un server della community) |
| lobby | 4/100 giocatori | una lobby prima che la partita inizi |
| match | 4/100 giocatori | una partita o un round in corso |

role (fino a 48 caratteri) è *nei panni di chi* sta giocando il giocatore: un mestiere, una classe o un personaggio. Ha un campo tutto suo invece di un’altra frase in state , perché viene mostrato come etichetta accanto al numero di giocatori.

details e state sono limitati a 128 caratteri ciascuno, name a 64. Gli a capo e i caratteri di controllo vengono rimossi. Un **URL per l’icona volutamente non è supportato**: verrebbe scaricato da ogni client che mostra la riga, trasformando uno stato in un segnale che riferisce al tuo server ogni membro di ogni community in cui è il giocatore.

## Il pulsante Unisciti ora

Metti un blocco join nella tua attività e gli altri membri vedono un pulsante **Unisciti ora** accanto allo stato. Ci sono due modi, e puoi combinarli.

### 1. Un secret (per i giochi nativi)

Imposta {"join":{"secret":"raid-42"}} . Quando qualcuno preme Unisciti ora, quel secret viene consegnato alla *sua* copia del tuo gioco, sulla sua macchina, abbinata tramite lo stesso game_id . Non viene aperto nessun URL e non viene invocato nessun gestore di schemi. Il tuo gioco lo raccoglie con:

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

Interroga con ?since=<cursor> così vedi ogni evento una sola volta. Se il gioco di chi ha premuto non è in esecuzione, non viene consegnato nulla: un buon motivo per offrire anche un URL.

### 2. Un URL https (per giochi web e link alle lobby)

Imposta {"join":{"url":"https://play.example.com/s/abc"}} e il pulsante apre quel link. **È accettato solo https .** Uno schema personalizzato ( steam:// , mygame:// , file:// ) viene rifiutato: quel blocco finisce sullo schermo di ogni membro, e un URL del genere è un modo per far invocare alla macchina di qualcun altro un gestore locale con argomenti scelti da te.

### Tutto ciò che sta in join è pubblico

Il blocco join viene trasmesso a chiunque possa vedere lo stato del giocatore; è proprio lo scopo di un pulsante Unisciti ora. Trattalo quindi come un codice di lobby, non come una credenziale. Non metterci mai nulla che debba restare segreto, e fai scadere i tuoi codici.

## FiveM

FiveM non ha HTTP nel suo runtime Lua lato client, quindi una resource dialoga con il bridge tramite **NUI**, una vista CEF, che invia un Origin. Il bridge accetta esplicitamente queste origini: https://cfx-nui-<resource> e il più vecchio nui://<resource> . Le normali pagine web restano rifiutate, e una pagina sul web aperto non può rivendicare quell’origine; la imposta il browser stesso.

```lua
-- La pagina NUI fa l’HTTP; Lua le manda solo i dati.
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: lo stato scade dopo 90 s
  end
end)
```

```javascript
const BASE = 'http://127.0.0.1:7440/mssgs/v1';   // prova 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']          // qui non serve altro
    })
  });
  const started = await res.json();
  if (started.status === 'approved') { return started.token; }

  // Ora il giocatore vede la finestra dei permessi in 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' } // il tuo link cfx.re
    })
  });
});
```

Il risultato: **Sta giocando a FiveM · Los Santos Roleplay · 4/100 giocatori · Police**, con un pulsante Unisciti ora che apre il tuo link cfx.re.

### Chiedi solo presence.write

Uno stato di gioco non ha bisogno d’altro: quello scope non legge assolutamente nulla. Se vuoi legare una ricompensa nel gioco all’appartenenza alla tua community mssgs, aggiungi membership.query e indica il tuo server_guid; continui a non sapere nulla delle altre community del giocatore.

### Un server su cui giochi non è automaticamente affidabile

Qualsiasi server FiveM può eseguire resource lato client, quindi qualsiasi server in cui qualcuno entra può chiedere il permesso. È proprio per questo che in mezzo c’è una finestra di dialogo che indica la resource: decide il giocatore, non il server.

## Giochi browser e per telefono: il collegamento tramite il tuo backend

Un gioco in una scheda del browser o su un telefono non può raggiungere il bridge qui sopra. Il bridge gira sul desktop del giocatore, e in mezzo ci sono tre muri: il bridge rifiuta ogni richiesta che porta un Origin del browser, Chrome mette una richiesta di permesso davanti a una pagina pubblica che contatta 127.0.0.1 e Safari la rifiuta del tutto, e un telefono non ha alcuna strada verso il loopback di un desktop.

Quindi la direzione si inverte. **Il tuo backend sa già chi sta giocando, e lo dice a mssgs**, per i giocatori che hanno collegato il proprio account mssgs al tuo gioco. Il collegamento viene approvato *nell’app mssgs*, mai nel tuo gioco, e crea un collegamento, mai una sessione: niente di quanto segue può far accedere qualcuno o agire al posto del giocatore. Il client del tuo gioco non vede mai una chiave e non parla mai con mss.gs. Il primo gioco su questa strada è [CozyCity](https://cozycity.net), un city builder distribuito come pagina WebGL e come app per iPhone, senza build desktop; gli esempi qui sotto sono i suoi.

### 1. Registra il tuo gioco

Registra il gioco nella [pagina di registrazione del Game SDK](https://mss.gs/it/docs/game-sdk/register): il tuo game_id (per esempio com.deverence.cozycity ), il nome e l’icona che il giocatore vede nella schermata di approvazione e gli hostname del tuo backend. Lo esaminiamo lì, e dopo l’approvazione la tua **chiave backend** ti aspetta sulla stessa pagina, mostrata una sola volta; noi ne conserviamo solo un digest. La chiave va sul tuo server e da nessun’altra parte. Puoi ruotarla lì in qualsiasi momento, e la vecchia resta valida per 24 ore così un deploy può completarsi.

[Registra il tuo gioco](https://mss.gs/it/docs/game-sdk/register)

Il nome e l’icona nella schermata **vengono sempre dalla registrazione**, mai dalla richiesta. Altrimenti un link di phishing potrebbe travestire una richiesta di collegamento da qualsiasi gioco volesse. Gli hostname delimitano dove può puntare un join.url , vedi più avanti.

### 2. Collega un giocatore

Il giocatore sceglie **Connetti mssgs** nel tuo gioco. Il tuo gioco chiede al tuo backend, e il tuo backend chiede a noi:

```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 è il tuo id stabile per quel giocatore (fino a 128 caratteri), non per una sessione o una partita; player_name (fino a 64) è ciò che la schermata mostra come “Giocatore: …”. Al client del tuo gioco passa solo link_code , qr_url e deep_link . device_code è il tuo riferimento per il polling e resta sul server.

Il tuo gioco mostra poi tre cose insieme, perché il giocatore potrebbe trovarsi ovunque:

- **Il QR** di qr_url . Un telefono con mssgs lo apre direttamente nella schermata di approvazione dell’app. Senza l’app arriva su una pagina di mss.gs che mostra il codice e propone il download.

- **Un pulsante “Apri in mssgs”** con deep_link , per un browser desktop accanto all’app desktop. È l’unico URL esterno che il tuo gioco debba mai aprire.

- **Il codice stesso**, in due gruppi di quattro, da digitare in **Impostazioni → Attività di gioco → Collega un gioco**. L’alfabeto non contiene 0/O né 1/I, quindi digitarlo raramente va storto.

Cosa vede il giocatore in mssgs, disegnato dall’app a partire dalla registrazione:

### Collegare CozyCity al tuo account mssgs?

CozyCity potrà mostrare a cosa stai giocando come stato mssgs. Non vedrà i tuoi messaggi, i tuoi amici o i tuoi server e non potrà pubblicare a tuo nome. Giocatore: *La città di René* · **Collega** / **Non ora**

Nel frattempo il tuo backend interroga ogni interval secondi (se va più veloce riceve 429 SLOW_DOWN ) finché lo stato non cambia. Un codice funziona una volta sola e scade dopo dieci minuti:

```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"}      # il giocatore ha scelto Non ora
# {"status":"expired"}
# {"status":"linked","link_guid":"…","user":{"user_guid":"62e377…"}}
```

Salva link_guid insieme al tuo giocatore; da qui in poi è l’indirizzo per cui pubblichi. La risposta contiene solo lo user_guid ; uno username viene aggiunto solo se la tua registrazione ha lo scope identity.link , e oltre a questo non c’è nulla. Una seconda approvazione per lo stesso player_ref **sostituisce** il collegamento precedente, quindi un giocatore del tuo gioco è un account mssgs. Lo stesso account mssgs può collegarsi a più giochi e a più player_ref di uno stesso gioco (un iPad di famiglia).

### 3. Pubblica lo stato di gioco

Lo stesso blocco del [bridge](#activity), con le stesse regole e gli stessi limiti, solo che ora è per collegamento e con la tua chiave backend:

```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}     # il blocco è cambiato ed è stato trasmesso
200 {"published":true,"changed":false}    # identico a quello salvato; è stato solo rinnovato il TTL
204                                        # salvato, ma il giocatore ora non è online in mssgs
410 {"error":"LINK_REVOKED"}               # il giocatore si è scollegato: elimina il collegamento
```

Tratta 200 e 204 allo stesso modo: salvato. { "activity": null } cancella il blocco, invialo quando il giocatore esce. Una differenza rispetto al bridge: l’host di join.url deve essere uno dei tuoi backend registrati (o un suo sottodominio), altrimenti ricevi 400 INVALID_PAYLOAD . Così un backend non può mettere sullo stato di un giocatore un pulsante Unisciti ora che porta dove quel giocatore non ha mai giocato.

### Heartbeat ogni 60 secondi, TTL 120

Uno stato pubblicato vive **120 secondi** senza un nuovo messaggio e poi scade da solo. Quindi rinvia lo stesso blocco ogni 60 secondi; un blocco invariato non costa nulla e rinnova solo il TTL. Se il tuo heartbeat si ferma, si ferma anche la riga “Sta giocando a …”, ed è proprio questo lo scopo.

Con centinaia di giocatori online, invia l’heartbeat in una sola chiamata, fino a 100 elementi alla volta. Ogni elemento riceve il proprio stato, così un giocatore che si è scollegato in mssgs non blocca mai gli altri novantanove:

```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 } ] }
```

### Come viene mostrato lo stato

- Identico a uno stato dal bridge: **Sta giocando a CozyCity · Lantern Hollow · 6/40 giocatori**, con Unisciti ora quando c’è un join.url . Il blocco viene marcato via: "backend" lato server, così un client può aggiungere “Condiviso dal server del gioco”.

- **Solo mentre il giocatore è online in mssgs.** Senza un client mssgs aperto l’account è offline e resta offline; il tuo backend non può far sembrare presente qualcuno. Questo impedisce anche che questa strada diventi un segnale “René è al computer”.

- Precedenza: **un gioco dentro l’app > un gioco sul bridge > il tuo backend**. Se il giocatore si mette a giocare a scacchi dentro mssgs mentre il tuo backend continua a inviare heartbeat, vincono gli scacchi, non chi ha scritto per ultimo.

### Scollegare

Il giocatore vede ogni collegamento in **Impostazioni → Attività di gioco → Giochi collegati**, con la tua icona e il tuo nome, il nome del giocatore dal tuo gioco, quando è stato collegato e quando ha pubblicato l’ultima volta, e un pulsante **Scollega**. Dopo di che la tua pubblicazione successiva risponde 410 LINK_REVOKED ; è così che il tuo gioco lo scopre. Elimina il link_guid e proponi di nuovo “Connetti mssgs”. Dalla tua parte, chiudi un collegamento con DELETE /game-sdk/v1/links/{link_guid} .

### Limiti

Per collegamento, una *modifica* conta al massimo ogni 2 secondi; un heartbeat invariato è gratuito. Per chiave ci sono 600 richieste al minuto, con gli elementi di un batch contati singolarmente: un heartbeat per 300 giocatori ogni 60 secondi ne consuma 5 su 600.

## Riferimento degli endpoint: il bridge

URL di base http://127.0.0.1:<port> . Tutto tranne i primi tre richiede Authorization: Bearer <token> .

| Metodo | Percorso | Scope | Cosa fa |
| --- | --- | --- | --- |
| GET | /mssgs/v1/hello | nessuno | mssgs è qui, che versione dell’API parla, e qualcuno ha fatto l’accesso? L’unica route che non richiede un token, e non dice nulla sul giocatore. |
| POST | /mssgs/v1/authorize | nessuno | Chiede il permesso al giocatore. Apre una finestra di dialogo nell’app e restituisce un request_id da interrogare. |
| GET | /mssgs/v1/authorize/:request_id | nessuno | pending, approved (con il token), denied o expired. |
| GET | /mssgs/v1/me | identity | Il giocatore con accesso effettuato. Aggiunge is_staff / is_moderator solo con lo scope staff. |
| GET | /mssgs/v1/membership | membership.query | Appartenenza ai valori server_guid che passi (fino a 10, ripetuti o separati da virgole). |
| GET | /mssgs/v1/servers | servers.list | Ogni community di cui fa parte il giocatore, con i suoi ruoli. I messaggi diretti non sono mai inclusi. |
| PUT | /mssgs/v1/activity | presence.write | Pubblica il blocco “Sta giocando a …”. Restituisce il TTL e ogni quanto inviare l’heartbeat. |
| POST | /mssgs/v1/activity/heartbeat | presence.write | Tiene viva l’attività pubblicata senza rinviarla. |
| DELETE | /mssgs/v1/activity | presence.write | La cancella subito, per una chiusura regolare. |
| GET | /mssgs/v1/events | presence.write | Passaggi di ingresso destinati al tuo gioco. Interroga con ?since=<cursor>. |
| GET | /mssgs/v1/session | nessuno | Cosa contiene questo token: game_id, scope concessi, se qualcuno ha fatto l’accesso. |
| DELETE | /mssgs/v1/session | nessuno | Restituisce il permesso. Stesso effetto del giocatore che lo revoca nelle Impostazioni. |

## Riferimento degli endpoint: backend collegati

URL di base https://ams1-gateway.mss.gs . Ogni route richiede Authorization: Bearer <backend key> e lo scope activity.write sulla tua registrazione; le risposte escono con Cache-Control: no-store . Chiamale dal tuo server, mai dal client del gioco.

| Metodo | Percorso | Cosa fa |
| --- | --- | --- |
| POST | /game-sdk/v1/link/start | Avvia un collegamento per uno dei tuoi giocatori ({ player_ref, player_name? }). Restituisce link_code, device_code, qr_url, deep_link, expires_in e interval. |
| POST | /game-sdk/v1/link/poll | { device_code } → pending, denied, expired, oppure linked con link_guid e user. |
| DELETE | /game-sdk/v1/links/{link_guid} | Chiude un collegamento dalla tua parte. Il giocatore può fare lo stesso dalle Impostazioni. |
| PUT | /game-sdk/v1/links/{link_guid}/activity | Pubblica il blocco “Sta giocando a …” per un giocatore; { "activity": null } lo cancella. |
| POST | /game-sdk/v1/activity/batch | Lo stesso, per fino a 100 giocatori in una chiamata. Ogni elemento risponde per conto suo. |

## Codici di errore

Gli errori tornano come {"error":"CODE","message":"…"} con lo stato HTTP corrispondente.

| Codice | Significato |
| --- | --- |
| 401 UNAUTHORIZED | Token mancante o sconosciuto; autorizza prima. |
| 403 MISSING_SCOPE | Il giocatore non ha concesso quel permesso. Potrebbe averlo deselezionato. |
| 403 ORIGIN_NOT_ALLOWED | La richiesta portava un Origin del browser. Vedi “Solo giochi nativi” più avanti. |
| 409 NOT_SIGNED_IN | mssgs è in esecuzione ma nessuno ha fatto l’accesso. |
| 429 RATE_LIMITED | Più di 120 richieste in un minuto da un solo gioco. |
| 400 INVALID_GAME_ID | game_id deve contenere solo lettere, cifre, punto, trattino o trattino basso. |
| 400 TOO_MANY_GUIDS | Al massimo 10 valori server_guid per chiamata di appartenenza. |

### Backend collegati

Le route del backend usano la stessa forma. Dentro un batch lo stato torna per ciascun elemento in results , così un collegamento chiuso non fa mai fallire l’intera chiamata.

| Codice | Significato |
| --- | --- |
| 401 INVALID_BACKEND_KEY | Chiave sconosciuta, o ruotata da più di 24 ore. |
| 403 SCOPE_NOT_GRANTED | La tua registrazione non ha lo scope richiesto da quella route. |
| 400 INVALID_PAYLOAD | Body non valido, più di 100 elementi nel batch, o un join.url il cui host non è uno dei tuoi backend registrati. |
| 400 INVALID_ACTIVITY | Dopo la normalizzazione non resta un name utilizzabile. |
| 410 LINK_REVOKED | Il collegamento è stato chiuso, da una delle due parti. Eliminalo e proponi di nuovo “Connetti mssgs”. |
| 429 SLOW_DOWN | Hai interrogato link/poll più spesso di interval. |
| 429 RATE_LIMITED | Una modifica a un collegamento entro 2 secondi dalla precedente, o più di 600 richieste in un minuto sulla tua chiave. |
| 503 LINK_STORE_UNAVAILABLE | Problema temporaneo da parte nostra. Riprova al prossimo heartbeat. |

## Sicurezza

### Solo giochi nativi

Le richieste che portano l’ Origin di una pagina web vengono rifiutate con 403 ORIGIN_NOT_ALLOWED . Che qualsiasi pagina web possa rilevare che usi mssgs e far comparire una finestra dei permessi è una superficie per fingerprinting e phishing, non una funzione. Un gioco nativo non invia alcun Origin, quindi non ne è toccato, e il browser integrato di un gioco è ammesso per nome, vedi [FiveM](#fivem). Se stai costruendo un gioco browser o per telefono non dialoghi con il bridge: è il tuo backend a pubblicare per i giocatori collegati, vedi [Giochi browser e per telefono](#linked).

### Cosa resta nelle mani del giocatore

- Il giocatore può spegnere il bridge in **Impostazioni → Attività di gioco**, dopo di che nessun gioco può più vedere mssgs.

- Ogni gioco approvato è elencato lì con esattamente i permessi che ha, quando è stato attivo l’ultima volta e un pulsante **Rimuovi**. La rimozione è immediata: il token smette subito di valere.

- Un gioco browser o per telefono collegato è elencato in **Giochi collegati** con un pulsante **Scollega**. Anche lo scollegamento è immediato: la pubblicazione successiva di quel backend riceve un 410 .

- Il bridge è in ascolto solo su 127.0.0.1, mai sulla rete.

- I messaggi diretti non vengono mai rivelati, nemmeno con servers.list.

- C’è un budget di 120 richieste al minuto per gioco.

### Comportarsi bene

- Chiedi gli scope quando ti servono, non tutti insieme al primo avvio.

- Funziona anche senza mssgs: il giocatore non è obbligato ad averlo.

- Cancella il tuo stato quando il gioco si ferma, invece di aspettare il TTL.

- Tratta uno scope rifiutato come un esito normale, non come un errore.

## Continua a costruire
