Vai al contenuto
Integrazioni per sviluppatori

Collega Puzzel alla tua piattaforma

Puzzel può inviare il risultato di un giocatore direttamente a un altro sistema — il suo punteggio, quanto ha completato, cosa ha risposto — senza che quel sistema sia un LMS completo. Questa pagina raccoglie tutti i canali disponibili: cosa viene inviato, quando viene inviato e il minimo che devi costruire per riceverlo.

I risultati escono tramite
Un webhook, oppure un messaggio alla pagina che circonda l'attività
La tua pagina può
Far accedere il giocatore e azzerare l'attività
Si attiva
Per singola attività, nel menu Sviluppatore dell'editor
Incluso con
Un piano a pagamento — su un account gratuito queste impostazioni sono disattivate

Di quale canale hai bisogno?

Tre cose possono uscire da un'attività e una può entrare. Quale ti serve dipende da una sola domanda: il giocatore si trova dentro la tua pagina, oppure altrove?

In uscita da Puzzel
In entrata verso Puzzel

Webhook dei risultati

Attiva "Invia i risultati a un webhook" nell'editor e indica un URL. Da quel momento, ogni volta che i progressi del giocatore vengono salvati, il suo browser invia in POST l'intero risultato a quell'URL, come JSON.

Come configurarlo
  1. 1 Apri l'attività nell'editor e vai al menu Sviluppatore.
  2. 2 Attiva "Invia i risultati a un webhook" e incolla il tuo endpoint nel campo sottostante. Deve essere un URL completo — un semplice dominio viene rifiutato — e deve essere https, perché il browser blocca una chiamata http semplice fatta da una pagina servita in https.
  3. 3 Gioca tu stesso l'attività una volta. Il primo POST arriva non appena rispondi a qualcosa.
Un ricevitore, dall'inizio alla fine
import express from 'express';

const app = express();

// The POST is made by the player's browser, so the browser asks permission
// first. Answer the preflight and the real request can land.
app.use((req, res, next) => {
  res.set('Access-Control-Allow-Origin', 'https://puzzel.org');
  res.set('Access-Control-Allow-Headers', 'Content-Type');
  if (req.method === 'OPTIONS') return res.sendStatus(204);
  next();
});

app.post('/puzzel/results', express.json(), async (req, res) => {
  const result = req.body;

  // One run posts several times as it goes. Key on the pair, not on an
  // insert: each call carries the whole state, so the newest simply wins.
  await db.results.upsert(
    { playerUid: result.playerUid, activityKey: result.activityKey },
    result
  );

  // Only act on the finish once.
  if (result.progress >= 100 || result.hasAlternateCompletion) {
    await markCourseStepComplete(result);
  }

  res.sendStatus(200);
});
Si attiva mentre giochi, non solo alla fine

Un risultato viene inviato ogni volta che la voce viene salvata: dopo una pausa nella digitazione, quando viene posizionata una carta, quando il tempo si ferma, e ancora una volta quando l'attività viene completata. Un cruciverba lungo genera una ventina di chiamate, non una sola — quindi scrivi il tuo gestore come un upsert basato su playerUid e activityKey, non come un inserimento. Ogni chiamata porta lo stato completo, quindi la più recente sostituisce sempre l'ultima e una chiamata persa viene compensata dalla successiva.

Distinguere un completamento da un salvataggio

progress è una percentuale: 100 significa che l'attività è completa. Alcuni tipi possono terminare senza raggiungerla — un quiz risposto fino in fondo, un campo soluzione risolto — e in quel caso portano invece hasAlternateCompletion. Considera entrambi i casi come completati.

Viene inviato dal browser del giocatore

Il POST arriva dalla scheda in cui si sta giocando l'attività, non da un server di Puzzel. La maggior parte degli endpoint costruiti per ricevere webhook lo accetta già. Se il tuo non riceve mai nessuna richiesta, ecco perché: il browser chiede prima il permesso, quindi rispondi al preflight OPTIONS con un'intestazione Access-Control-Allow-Origin, e il vero POST arriverà subito dopo.

Tratta il payload come una dichiarazione, non come una prova

La richiesta non ha una firma e proviene da un browser che non controlli, quindi chiunque guardi la pagina può inviartene una anche lui. Va benissimo per riempire una barra di avanzamento o una dashboard. Per qualsiasi cosa che non lasceresti impostare a uno studente in autonomia — un voto che conta, un attestato, un pagamento — verificalo con i risultati nella tua dashboard di Puzzel, oppure lascia che siano i connettori di voto per LMS a trasmettere il punteggio.

Risultati alla pagina che la circonda

Se incorpori l'attività, puoi far arrivare lo stesso JSON alla tua pagina invece che a un server. Attiva "Invia i risultati alla pagina principale" e resta in ascolto del messaggio. Nulla esce dal browser, quindi non c'è nessun endpoint da costruire e nessun CORS di cui preoccuparti.

index.html
<iframe
  src="https://puzzel.org/en/quiz/embed?p=-Nq8sample_activity_key"
  width="100%"
  height="700"
  frameborder="0"
  allowfullscreen
></iframe>

<script>
  window.addEventListener('message', (event) => {
    if (event.origin !== 'https://puzzel.org') return;

    const result = event.data;
    if (!result || !result.activityKey) return;

    // Same shape the webhook posts, same advice: it arrives repeatedly.
    saveProgress(result);
  });
</script>
Verifica da dove proviene il messaggio

Il tuo listener riceve ogni messaggio inviato alla pagina, comprese le estensioni del browser e altri frame. Confronta event.origin con https://puzzel.org prima di fidarti del suo contenuto.

Lo stesso payload, la stessa tempistica

È il gemello del webhook: gli stessi campi, inviati negli stessi momenti. Tutto quanto scritto in "Cosa contiene un risultato" vale anche qui.

Segnale di completamento

Il canale più piccolo, per quando il risultato in sé non ti riguarda: attiva "Invia un segnale di completamento" e la tua pagina riceve un messaggio nel momento in cui il giocatore finisce.

index.html
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://puzzel.org') return;
  if (event.data?.completed !== true) return;

  // { completed: true, activityKey: '-Nq8sample_activity_key' }
  unlockNextLesson(event.data.activityKey);
});
Cosa arriva
{
  "completed": true,
  "activityKey": "-Nq8sample_activity_key"
}
Il risultato è già salvato quando arriva

Il segnale viene inviato deliberatamente dopo che il salvataggio di completamento è avvenuto, quindi una pagina che reagisce rileggendo il risultato lo troverà già disponibile.

Solo dentro un iframe

Entrambi i canali a messaggi inviano alla pagina che incornicia l'attività. Se aperta nella propria scheda non c'è nessuno a cui dirlo, quindi non viene inviato nulla.

Cosa contiene un risultato

Una sola struttura, qualunque sia il canale che la trasporta. Le risposte del giocatore sono indicizzate dagli id degli elementi dell'attività, quindi le stesse chiavi compaiono anche in correctUids.

CampoTipoCosa fa
activityKey
sempre
string
stringL'attività a cui appartiene il risultato. La stessa chiave che vedi nell'URL dell'attività, dopo ?p=.
playerUid
sempre
string
stringChi ha giocato, come id anonimo. Stabile per questo giocatore su questo dispositivo, quindi è ciò su cui basare l'indicizzazione dei risultati — non è un indirizzo e-mail né un account Puzzel.
player
a volte
object
objectI campi di registrazione richiesti dall'attività, come li hai configurati: nome, e-mail, classe, student_id e così via. Assente finché il giocatore non si è registrato, e del tutto assente su un'attività che non chiede nulla.
progress
sempre
number
numberQuanto è avanzato, in percentuale. 100 significa completato.
timePassed
sempre
number
numberTempo trascorso sull'attività, in millisecondi.
lastPlayedAt
sempre
number
numberQuando questo risultato è stato salvato, come timestamp Unix in millisecondi.
createdAt
a volte
number
numberQuando il tentativo è iniziato, come timestamp Unix in millisecondi.
playerInput
a volte
object
objectCiò che il giocatore ha effettivamente inserito, indicizzato dall'id dell'elemento a cui appartiene. La struttura interna dipende dal tipo di attività — una parola, un elenco di carte posizionate, un'opzione scelta.
correctUids
a volte
object
objectQuali di quegli elementi sono corretti, indicizzati allo stesso modo. Assente finché non è stata data ancora nessuna risposta.
score
a volte
number
numberPunti ottenuti, sui tipi che assegnano un punteggio a un tentativo. Assente in tutti gli altri casi — incluso un tentativo che ha davvero ottenuto zero punti, quindi verifica che la chiave esista prima di leggerla.
performance
a volte
number
numberUna misura specifica del tipo di quanto sia andato bene, dove ne tiene una — ad esempio le parole al minuto nell'esercizio di digitazione.
attempts
a volte
number
numberA quale tentativo corrisponde: 1 la prima volta, uno in più a ogni nuovo inizio. Solo sui tipi che possono terminare un tentativo in anticipo e ne contano le ripetizioni.
knockedOut
a volte
boolean
booleanIl tentativo si è concluso con una risposta sbagliata ed è terminato senza essere completo.
hasAlternateCompletion
a volte
boolean
booleanL'attività è stata completata in un modo che non raggiunge il 100% — un quiz risposto fino in fondo, un campo soluzione risolto. Considerala comunque un completamento.
missedKeys
a volte
array
arrayI caratteri che il giocatore ha continuato a sbagliare, in ordine di frequenza. Solo nell'esercizio di digitazione.
contentVersion
a volte
number
numberA quale versione del contenuto dell'attività si riferisce questo tentativo. Cambia quando il proprietario modifica le domande, così un risultato vecchio si può distinguere da uno attuale.
Un quiz completato, così come arriva
{
  "activityKey": "-Nq8sample_activity_key",
  "playerUid": "kK3r9TzSampleAnonymousUid",
  "player": {
    "name": "Ava Ortega",
    "email": "ava@school.example",
    "class": "5B"
  },
  "progress": 100,
  "timePassed": 243120,
  "lastPlayedAt": 1758297843120,
  "createdAt": 1758297600000,
  "playerInput": {
    "-Nq8item_one": "Lisbon",
    "-Nq8item_two": "1969"
  },
  "correctUids": {
    "-Nq8item_one": true,
    "-Nq8item_two": false
  },
  "score": 800,
  "contentVersion": 1757923200000
}
Assente, non vuoto

Un campo che non si applica viene omesso dal JSON invece di essere inviato come null o zero. È così che un tipo che non assegna un punteggio si distingue da un tentativo che ha ottenuto zero punti — quindi leggi sempre con un valore predefinito e non dare mai per scontato che una chiave sia presente.

Azzerare l'attività dalla tua pagina

Un'istruzione viaggia nella direzione opposta. Con "Accetta comandi dalla pagina principale" attivato, la pagina che esegue l'incorporamento può cancellare le risposte del giocatore e riportare l'attività all'inizio — per un tuo pulsante "riprova" esterno al frame.

reset
const frame = document.querySelector('iframe').contentWindow;

// Sent to the frame, from the page that embeds it — no other sender is
// accepted, and the activity has to have the trigger setting switched on.
frame.postMessage({ trigger: { type: 'reset' } }, 'https://puzzel.org');
L'azzeramento è l'unico comando

Non esiste un messaggio per inviare un risultato, aprire il messaggio finale o passare a una domanda. Un messaggio che chiede qualcosa di diverso da un azzeramento viene ignorato.

Viene ascoltata solo la pagina che incorpora

Il comando viene accettato solo dalla pagina che incornicia l'attività e da nessun'altra parte — non un frame vicino, non uno script sulla pagina. L'impostazione è disattivata per impostazione predefinita, quindi attivala per le attività che controlli.

Portare la propria identità del giocatore

Se la tua piattaforma sa già chi sta giocando, l'attività non deve chiederglielo di nuovo. Esistono due handshake, entrambi per un'attività dentro la tua pagina, ed entrambi attivati da noi anziché nell'editor — cambiano a chi appartiene un risultato, quindi vengono configurati insieme a te e non da una casella di spunta.

Inviaci il giocatore

L'attività si annuncia con 'app-loaded' e resta in attesa. La tua pagina rimanda indietro i dati del giocatore, e il risultato viene archiviato sotto di essi senza che il giocatore digiti nulla o veda una schermata di registrazione.

Inviaci un token

Lo stesso handshake, ma la tua pagina invia il JWT emesso dal tuo provider di identità invece dei singoli campi. Lo verifichiamo rispetto agli emittenti configurati per il tuo account prima di far entrare il giocatore, quindi l'identità è dimostrata e non solo dichiarata — è questo da chiedere quando il risultato deve essere affidabile.

index.html
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://puzzel.org') return;

  // The activity says it is ready for a player.
  if (event.data === 'app-loaded') {
    frame.postMessage(
      {
        player: { name: 'Ava Ortega', email: 'ava@school.example', class: '5B' },
        // true starts a fresh attempt instead of resuming this player's last one
        should_reset: false
      },
      'https://puzzel.org'
    );
  }

  // Signed in; the board is coming.
  if (event.data?.success === true) hideYourOwnSpinner();
});
Chiedici di attivarlo

Dicci quale dei due vuoi e dove verranno incorporate le attività, e configureremo il tuo account guidandoti passo dopo passo.

Scrivici per l'identità

Creare attività dal tuo sistema

Tutto quanto sopra riguarda un risultato in uscita. Fare il percorso inverso — creare le attività stesse a partire da contenuti che hai già — è compito della Puzzle API: un POST per ogni tipo di attività, e in cambio ricevi una chiave e un URL da incorporare.

Leggi il riferimento API

Quando un connettore già pronto è la soluzione migliore

Se la piattaforma dall'altra parte è un vero LMS, probabilmente non ti serve niente di tutto questo. I voti possono tornare da soli al suo registro, senza che tu debba ospitare nulla.

Non è nessuno di questi?

Piattaforme per corsi, siti ad accesso riservato, intranet e qualsiasi cosa tu abbia costruito da solo sono esattamente ciò per cui servono i canali di questa pagina. Un incorporamento più il segnale di completamento copre la maggior parte dei casi.

Cosa non c'è

Così non lo cerchi invano:

  • Nessun endpoint per rileggere i risultati. La API crea le attività; i risultati escono tramite i canali di questa pagina, oppure tramite le esportazioni nella tua dashboard.
  • Nessuna firma sul webhook. Non c'è nulla su cui verificare la richiesta, ed è per questo che un risultato non dovrebbe essere l'unica cosa a garantire qualcosa di importante.
  • Nessun webhook a livello di account. L'URL è un'impostazione di una singola attività, quindi un'attività che copi la porta con sé, mentre una nuova ne parte priva.
  • Niente in un gioco a squadre o in una stanza dal vivo. Sia i canali a messaggi sia il webhook sono pensati per il gioco singolo, e le impostazioni si disattivano da sole quando il gioco a squadre è attivo.
  • Nessuna coda di consegna. Niente viene memorizzato e reinviato — il prossimo salvataggio funge da nuovo tentativo, e l'ultima chiamata del tentativo è quella che conta.

Qualcosa non funziona come dovrebbe?

Inviaci la richiesta che hai provato, senza la chiave API, e il messaggio di errore ricevuto. Ti risponderà direttamente chi ha sviluppato l'endpoint.

Scrivi all'assistenza