Kobl Puzzel til din egen platform
Puzzel kan sende en spillers resultat direkte videre til et andet system — deres score, hvor langt de kom, hvad de svarede — uden at det system behøver at være et helt LMS. Denne side er alle kanalerne, der findes: hvad der sendes ud, hvornår det sendes ud, og det mindste, du selv skal bygge for at tage imod det.
- Resultater sendes ud via
- En webhook, eller en besked til siden omkring aktiviteten
- Din side kan
- Logge spilleren ind og nulstille aktiviteten
- Slås til
- Pr. aktivitet, under Udvikler i editoren
- Inkluderet med
- En betalt plan — disse indstillinger er slået fra på en gratis konto
Hvilken kanal har du brug for?
Tre ting kan forlade en aktivitet, og én kan komme ind. Hvilken der passer, afhænger af ét spørgsmål: sidder spilleren inde i din side, eller et helt andet sted?
Hvert gemt resultat sendes med POST til en URL, du selv ejer, som JSON.
- Brug den når
- Spilleren kan være hvor som helst — et delt link, en QR-kode, en andens hjemmeside — og du vil have resultatet i din egen database.
- Du skal bruge
- Et https-endpoint, der accepterer et cross-origin-POST.
save_puzzle_results_via_webhookSamme JSON, sendt til den side, der indlejrer aktiviteten, i stedet for til en server.
- Brug den når
- Du indlejrer aktiviteten på din egen kursusside, og siden selv kan gøre noget med resultatet.
- Du skal bruge
- En iframe på din side og en listener til beskeder. Ingen server, ingen CORS.
save_results_iframe_postmessageÉn besked, når spilleren er færdig, og den indeholder ikke andet end det faktum.
- Brug den når
- Alt, du vil vide, er, om de er færdige — for at afkrydse lektionen, låse den næste op eller vise din egen skærm.
- Du skal bruge
- En iframe på din side og en listener til beskeder.
send_completion_signal_when_embeddedResultatwebhook
Slå "Send resultater til en webhook" til i editoren, og giv den en URL. Fra da af sender spillerens browser hele resultatet som JSON med POST til den URL, hver gang deres fremskridt gemmes.
- 1 Åbn aktiviteten i editoren, og gå til menuen Udvikler.
- 2 Slå "Send resultater til en webhook" til, og indsæt dit endpoint i feltet nedenunder. Det skal være en fuld URL — et rent domæne bliver afvist — og det skal være https, fordi browseren blokerer et almindeligt http-kald fra en side, der serveres over https.
- 3 Spil aktiviteten selv én gang. Det første POST-kald lander, så snart du svarer på noget.
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);
});Et resultat sendes ud, hver gang indtastningen gemmes: efter en pause i skrivningen, når et kort placeres, når uret stopper, og igen når aktiviteten er færdig. Et langt krydsord er et par dusin kald, ikke ét — så skriv din handler som en upsert med nøgle på playerUid og activityKey i stedet for som en insert. Hvert kald indeholder hele tilstanden, så det nyeste altid overskriver det sidste, og et kald, der går tabt, bliver rettet op af det næste.
progress er en procentdel: 100 betyder, at aktiviteten er fuldført. Nogle få typer kan slutte uden at nå dertil — en quiz besvaret helt igennem, et løst løsningsfelt — og de bærer i stedet hasAlternateCompletion. Behandl begge dele som færdig.
POST-kaldet kommer fra den fane, aktiviteten spilles i, ikke fra en Puzzel-server. De fleste endpoints, der er bygget til at modtage webhooks, accepterer det allerede. Hvis dit aldrig ser et kald, er det derfor: browseren spørger om lov først, så besvar OPTIONS-preflighten med en Access-Control-Allow-Origin-header, og det rigtige POST-kald følger efter.
Der er ingen signatur på kaldet, og det kommer fra en browser, du ikke selv styrer, så alle, der kigger på siden, kan sende dig ét ligeså. Det er fint til at udfylde en fremskridtsbjælke eller et dashboard. For alt, du ikke ville lade en elev sætte selv — en karakter, der tæller, et certifikat, en betaling — så tjek det mod resultaterne i dit eget Puzzel-dashboard, eller lad LMS'ets karakterforbindelse overføre scoren i stedet.
Resultater til siden omkring den
Hvis du indlejrer aktiviteten, kan du få den samme JSON sendt til din egen side i stedet for til en server. Slå "Send resultater til den overordnede side" til, og lyt efter beskeden. Intet forlader browseren, så der er intet endpoint at bygge og ingen CORS at tænke på.
<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>Din listener hører hver eneste besked, der sendes til siden, også fra andre frames og browserudvidelser. Sammenlign event.origin med https://puzzel.org, før du stoler på indholdet.
Dette er webhookens tvilling: de samme felter, sendt på de samme tidspunkter. Alt under "Hvad et resultat indeholder" gælder også her.
Afslutningssignal
Den mindste kanal, til når selve resultatet ikke er din sag: slå "Send et afslutningssignal" til, og din side får én besked, i det øjeblik spilleren er færdig.
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);
});{
"completed": true,
"activityKey": "-Nq8sample_activity_key"
}Signalet sendes bevidst, efter den afsluttende gemning er landet, så en side, der reagerer ved at læse resultatet tilbage, vil finde det der.
Begge beskedkanaler sender til den side, der rammer aktiviteten ind. Åbnet i sin egen fane er der ingen at fortælle det til, så der bliver ikke sendt noget.
Hvad et resultat indeholder
Én form, uanset hvilken kanal der bærer den. Spillerens svar er nøglet efter aktivitetens egne item-id'er, så de samme nøgler dukker op i correctUids.
| Felt | Type | Hvad det gør |
|---|---|---|
activityKey altid string | string | Den aktivitet, resultatet hører til. Samme nøgle, som du ser i aktivitetens egen URL, efter ?p=. |
playerUid altid string | string | Hvem der spillede, som et anonymt id. Stabilt for denne spiller på denne enhed, så det er det, du nøgler resultater på — det er ikke en e-mailadresse og ikke en Puzzel-konto. |
player nogle gange object | object | De tilmeldingsfelter, aktiviteten beder om, sådan som du har konfigureret dem: name, email, class, student_id og så videre. Fraværende, indtil spilleren har tilmeldt sig, og helt fraværende på en aktivitet, der ikke beder om noget. |
progress altid number | number | Hvor langt spilleren er nået, som en procentdel. 100 betyder færdig. |
timePassed altid number | number | Tid brugt på aktiviteten, i millisekunder. |
lastPlayedAt altid number | number | Hvornår dette resultat blev gemt, som et Unix-timestamp i millisekunder. |
createdAt nogle gange number | number | Hvornår forsøget blev startet, som et Unix-timestamp i millisekunder. |
playerInput nogle gange object | object | Hvad spilleren faktisk indtastede, nøglet efter id'et på det item, det hører til. Formen indeni afhænger af aktivitetstypen — et ord, en liste over placerede kort, en valgt mulighed. |
correctUids nogle gange object | object | Hvilke af de items der er rigtige, nøglet på samme måde. Fraværende, så længe der ikke er svaret på noget endnu. |
score nogle gange number | number | Point scoret, på de typer, der giver point for en runde. Fraværende alle andre steder — også på en runde, der reelt scorede nul, så tjek, at nøglen findes, før du læser den. |
performance nogle gange number | number | En types eget mål for, hvor godt det gik, hvor den holder ét — for eksempel ord i minuttet i tastetræning. |
attempts nogle gange number | number | Hvilken runde det er: 1 første gang, én mere for hver gang, der startes forfra. Kun på typer, der kan afslutte en runde tidligt og tæller de gentagne forsøg. |
knockedOut nogle gange boolean | boolean | Runden sluttede på et forkert svar og er slut uden at være gennemført. |
hasAlternateCompletion nogle gange boolean | boolean | Aktiviteten blev afsluttet på en måde, der ikke når 100 % — en quiz besvaret helt igennem, et løsningsfelt løst. Behandl det som en gennemførelse. |
missedKeys nogle gange array | array | Tegn, spilleren blev ved med at ramme forkert, de mest fejlramte først. Kun i tastetræning. |
contentVersion nogle gange number | number | Hvilken version af aktivitetens indhold, dette blev spillet mod. Den ændrer sig, når ejeren redigerer spørgsmålene, så et gammelt resultat kan kendes fra et aktuelt. |
{
"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
}Et felt, der ikke er relevant, udelades af JSON'en i stedet for at blive sendt som null eller nul. Sådan kan en type, der ikke giver point for en runde, kendes fra en runde, der scorede nul — så læs med en standardværdi, og gå aldrig ud fra, at en nøgle er der.
Nulstil aktiviteten fra din side
Én instruktion rejser den anden vej. Med "Modtag triggere fra den overordnede side" slået til kan den side, der indlejrer, rydde spillerens svar og sætte aktiviteten tilbage til starten — til din egen "prøv igen"-knap uden for frame'n.
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');Der er ingen besked til at indsende et resultat, åbne afslutningsbeskeden eller hoppe til et spørgsmål. En besked, der beder om andet end en nulstilling, bliver ignoreret.
Triggeren accepteres kun fra den side, der rammer aktiviteten ind, og ingen andre steder fra — ikke en søsterframe, ikke et script på siden. Indstillingen er slået fra som standard, så slå den til for de aktiviteter, du styrer.
Medbring din egen spilleridentitet
Hvis din platform allerede ved, hvem der spiller, behøver aktiviteten ikke spørge igen. Der findes to håndtryk, begge til en aktivitet inde i din side, og begge slås til af os frem for i editoren — de ændrer, hvem et resultat hører til, så de sættes op sammen med dig frem for via et afkrydsningsfelt.
Aktiviteten melder sig selv med 'app-loaded' og venter. Din side sender spillerens oplysninger tilbage, og resultatet arkiveres under dem, uden at spilleren skal skrive noget eller se en tilmeldingsskærm.
Samme håndtryk, men din side sender den JWT, din identitetsudbyder har udstedt, i stedet for selve felterne. Vi verificerer den mod de issuers, der er sat op for din konto, før spilleren lukkes ind, så identiteten er bevist frem for hævdet — det er den, du skal bede om, når resultatet skal være troværdigt.
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();
});Fortæl os, hvilken af de to du vil have, og hvor aktiviteterne skal indlejres, så sætter vi din konto op og gennemgår det sammen med dig.
Skriv til os om identitetOpret aktiviteter fra dit eget system
Alt ovenfor handler om, at et resultat kommer ud. Den anden vej — at lave selve aktiviteterne ud fra indhold, du allerede har — er Puzzle API'et: ét POST-kald pr. aktivitetstype, og du får en nøgle og en URL til indlejring tilbage.
Læs API-referencenNår en færdig forbindelse er det bedre svar
Hvis platformen i den anden ende er et rigtigt LMS, har du sandsynligvis ikke brug for noget af dette. Karakterer kan gå tilbage til dets karakterbog af sig selv, uden at du skal hoste noget.
Startet inde fra LMS'et, med scoren skrevet tilbage til dets karakterbog.
Post en aktivitet som en opgave, og få karaktererne tilbage automatisk.
Kursusplatforme, medlemssider, intranet og alt, du selv har bygget, er lige præcis det, kanalerne på denne side er til. En indlejring plus afslutningssignalet dækker det meste.
Hvad der ikke findes
Så du ikke går og leder efter det:
- Intet endpoint til at læse resultater tilbage. API'et opretter aktiviteter; resultater forlader systemet gennem kanalerne på denne side eller gennem eksporterne i dit dashboard.
- Ingen signatur på webhooken. Der er intet at verificere kaldet mod, og derfor bør et resultat ikke være det eneste, der står bag noget, der betyder noget.
- Ingen kontobred webhook. URL'en er en indstilling på en aktivitet, så en aktivitet, du kopierer, tager den med, og en ny starter uden den.
- Ingenting i et holdspil eller et live-rum. Både beskedkanalerne og webhooken er til solospil, og indstillingerne slår sig selv fra, når holdspil er slået til.
- Ingen leveringskø. Intet bliver gemt og sendt igen — den næste gemning er forsøget igen, og rundens sidste kald er det, der tæller.
Opfører noget sig ikke, som det skal?
Send den anmodning, du prøvede, og den fejl, du fik tilbage, så får du et rigtigt svar — fra den person, der skrev endpointet.
Skriv til support