Tempo reale (WebSocket)
Il WebSocket della piattaforma - il ticket monouso, il sottoprotocollo memberfy-realtime, le stanze automatiche e quelle richieste, gli avvisi invalidate, i limiti e i codici di chiusura.
L'app riceve i cambiamenti subito tramite un WebSocket: la campanella, la chat, l'"Online adesso", chi c'è in ogni schermata, il banner dell'evento in diretta, i numeri delle campagne. Qualsiasi membro della community, di qualsiasi ruolo, apre il socket, con la chat attiva o disattivata.
Aprire la connessione
- Chiedi un ticket con
POST /api/realtime/ticket, con il token e l'X-CommunityId(vedi Autenticazione e X-CommunityId). Il ticket è monouso, vale 60 secondi ed è legato alla persona, alla community e alla sessione. - Apri
wss://<api>/api/realtime/socket(ildata.path) passando il ticket come secondo sottoprotocollo, dopomemberfy-realtime(ildata.protocols). Il server risponde conmemberfy-realtime.
const { data } = await api.post('/api/realtime/ticket'); // con Authorization e X-CommunityId
const ws = new WebSocket(`wss://api.memberfy.net${data.path}`, data.protocols);
Il ticket non va mai nell'URL (nella query viene ignorato). Senza un ticket valido, la connessione viene rifiutata prima di diventare WebSocket (401). Cambiare community chiude il socket: chiedi un altro ticket. Senza il servizio di tempo reale, il ticket restituisce 503, e l'app continua a leggere tramite le rotte.
POST /api/chat/ticket e /api/chat/socket continuano a funzionare come alias di questi per un deploy e poi spariscono: usa i nuovi.
Stanze
Ogni connessione entra già in tre stanze:
| Stanza | Chi |
|---|---|
me | La persona stessa |
community | Tutti nella community |
staff | Solo il team (proprietario, amministratore, moderatore e finanza) |
Le altre le chiede il client, con la stessa regola del GET corrispondente, sempre in questa community:
| Stanza | Chi entra |
|---|---|
space:<id> | Chi può aprire lo spazio |
item:event:<id> | Chi può aprire l'evento |
item:post:<id> | Chi può aprire il post |
{ "type": "subscribe", "rooms": ["space:5b0e7c1a-…", "item:event:9d2f…"] }
{ "type": "unsubscribe", "rooms": ["space:5b0e7c1a-…"] }
Ci sono anche stanze per gli editor, solo per i ruoli che possono salvare lì: item:course:<id>, item:form:<id> (il team e chi gestisce il modulo; un membro quando è pubblicato e apre lo spazio), item:campaign:<id>, item:email:<id> (e-mail della libreria), item:workflow:<id>, item:survey:<id> (proprietario e amministratore), item:certificateTemplate:<id> (chi emette certificati nello spazio), item:emailOverride:<emailKey> (e-mail predefinita, proprietario e amministratore). E le stanze di area, una per schermata di sezione: area:home (tutti), area:members, area:campaigns, area:emails, area:workflows (team), area:moderation (proprietario, amministratore e moderatore), area:analytics, area:dashboard, area:settings (proprietario e amministratore) e area:finance (proprietario, amministratore e finanza).
Al massimo 20 stanze per connessione (quelle automatiche non contano). La risposta è subscribed con le stanze accettate e un subscribe.denied per stanza rifiutata, senza motivo. Quando l'accesso della persona cambia (è uscita, nuovo ruolo, gruppo, accesso allo spazio), il server rivaluta le stanze, manda subscribe.denied per quelle perse e poi access.changed.
Avvisi di cambiamento (invalidate)
{ "type": "invalidate", "room": "community", "topic": "event.live", "ids": ["9d2f…"], "at": "2026-10-23T14:05:00.000Z" }
L'avviso porta solo gli id e l'argomento, mai titolo, testo, importo o nome: quando lo ricevi, rifai il GET di ciò che è sullo schermo tramite le rotte di sempre.
| Argomento | Stanza | Quando |
|---|---|---|
event.live | community, space:<id>, item:event:<id> | Un evento ha iniziato o finito la trasmissione |
events | community, space:<id>, item:event:<id> | Un evento è stato creato, modificato o rimosso |
analytics.online | staff (proprietario e amministratore) | L'"Online adesso" è cambiato (al massimo 1 volta ogni 5 secondi) |
campaigns.stats | staff | Un invio di campagna è terminato |
menu.changed | community | Il menu è cambiato: sezione o spazio creato, modificato (nome, icona, indirizzo, sezione, accesso, visibilità), rimosso o riordinato. Senza ids e senza nessun dato del menu: ogni persona rifà il GET /api/sections con i propri permessi. Modifiche in sequenza diventano un solo avviso, entro 1 secondo |
Gli avvisi degli eventi arrivano solo a chi vede quello spazio; un evento privato o in moderazione, solo al team.
Chi c'è qui
La presenza di area (i volti in cima a ogni schermata, vedi Chi c'è qui e modifica in team) usa le stesse stanze.
{ "type": "presence.join", "room": "area:campaigns", "editing": false }
{ "type": "presence.leave", "room": "area:campaigns" }
{ "type": "presence.active" }
presence.joinall'apertura della schermata: la stanza viene valutata come insubscribe(e sottoscritta, se non lo era), al massimo 3 posti per connessione. La risposta èpresence.joined(room,editing) seguito da un'istantaneapresence, oppurepresence.denied(room), senza motivo.presence.leaveall'uscita. Non c'è heartbeat: il ping del server mantiene il posto, che sparisce 45 secondi dopo l'ultima risposta; chiudere la connessione lo toglie subito.presence.activequando c'è interazione (tasto, clic, focus), al massimo ogni 10 secondi. Entrare conta come attività.
A ogni cambiamento (al massimo 1 al secondo per stanza), la stanza riceve un'istantanea costruita per ogni connessione:
{
"type": "presence", "room": "area:campaigns", "area": "area:campaigns",
"members": [{ "memberId": "…", "name": "Maria", "avatarUrl": "…", "editing": false, "state": "active", "lastActiveAt": "…" }],
"count": 3, "editingCount": 1, "at": "2026-10-28T14:05:00.000Z"
}
- Tu non sei mai nella tua lista.
memberscontiene al massimo 5 persone (prima chi modifica ed è attivo, poi gli attivi, poi gli inattivi);countè il totale delle altre persone nella stanza. - Chi legge soltanto: nelle stanze di spazio e di elemento, chi non sta modificando lì riceve solo
memberId,name,avatarUrlestate, senzaediting,lastActiveAtnéeditingCount, per la privacy di chi modifica. Chi modifica lo stesso elemento e le stanze di area del team ricevono l'istantanea completa. state: "idle": connessa, senza attività da 30 secondi o più. Resta nella lista.- Privacy: chi ha disattivato Disponibile a chattare non compare né conta per gli altri, ma vede chi c'è. Un blocco nella chat nasconde le due persone l'una all'altra.
- Chat della community disattivata: le stanze di area e chi legge soltanto non ricevono nessuno (
membersvuoto,count0), team compreso. Solo chi modifica lo stesso elemento continua a vedere gli altri editor;item.savede il409di versione non cambiano.
Modifica e item.saved
In un editor (evento, campagna, e-mail della libreria, workflow, corso, modulo, sondaggio, modello di certificato, e-mail predefinita e le impostazioni, in area:settings), entra con "editing": true. Vale solo per chi può salvare lì (il team e, nel modulo, chi lo gestisce). Chi modifica compare con editing: true agli altri editor della stanza, anche con lo stato della chat disattivato; chi legge solo la pagina non lo sa. editingCount conta solo chi modifica ed è attivo.
A ogni salvataggio, la stanza riceve, per il team e per chi sta modificando:
{ "type": "item.saved", "topic": "item.saved", "room": "item:campaign:<id>", "itemType": "campaign", "itemId": "<id>", "by": { "memberId": "…", "name": "Maria" }, "updatedAt": "<nuova versione>", "at": "…" }
Confronta updatedAt con la versione che hai per ignorare il tuo salvataggio. itemType: event, campaign, email, workflow, course (inclusi moduli e lezioni), form, survey, certificateTemplate, emailOverride (itemId = emailKey) e, nella stanza area:settings (itemId = la community), settings, chatSettings, notificationSettings, messagingSettings (SMS + WhatsApp), creditSettings, businessInformation, payoutSettings.
I salvataggi accettano la versione letta e rispondono 409 quando un altro è arrivato prima: vedi Conflitto di versione.
Altri eventi
Dal server, in JSON { type, data }: ready (con profileId, communityId, chatAvailable, chatStatus (lo stato scelto in chat), chatEnabled, rooms, maxRooms e pingIntervalMs), notification.new, notification.read e notification.removed (la campanella, in ogni community), chat.settings, session.revoked e pong. Gli eventi della chat arrivano solo con la chat attiva; con la chat disattivata, il socket resta in piedi e si fermano solo quelli.
Dal client: subscribe, unsubscribe, presence.join, presence.leave, presence.active, presence.idle ({ "type": "presence.idle", "idle": true } quando la scheda resta inattiva per 10 minuti, false quando torna: la persona risulta Assente finché tutte le sue schede sono inattive), typing (solo con la chat attiva) e ping.
Limiti e codici di chiusura
- Messaggi fino a 4 KB, niente binario, al massimo 5 al secondo.
- Il server manda un
pingogni 25 secondi. - Per riconnetterti, aspetta un tempo casuale e crescente e chiedi un ticket nuovo.
| Codice | Cosa significa | Cosa fare |
|---|---|---|
4004 | Sessione terminata | Non riconnetterti con la stessa sessione |
4009 | Sostituita: la persona ha aperto più di 10 connessioni e si è chiusa la più vecchia | Niente, se è la scheda vecchia |
4029 | Server pieno | Riconnettiti più tardi e usa le rotte nel frattempo |
1008 | Troppi messaggi | Rallenta |
1001 | Server in riavvio | Riconnettiti |