Hopp til innhold
Utviklerintegrasjoner

Koble Puzzel til din egen plattform

Puzzel kan sende en spillers resultat rett videre til et annet system — poengsummen, hvor langt de kom, hva de svarte — uten at systemet må være en fullverdig LMS. Denne siden viser alle kanalene som finnes: hva som sendes ut, når det sendes ut, og det minste du må bygge for å ta imot det.

Resultater sendes ut via
En webhook, eller en melding til siden rundt aktiviteten
Siden din kan
Logge spilleren inn og tilbakestille aktiviteten
Slås på
Per aktivitet, under Utvikler i editoren
Inkludert med
Et betalt abonnement — disse innstillingene er avslått på en gratis konto

Hvilken kanal trenger du?

Tre ting kan forlate en aktivitet, og én kan komme inn. Hvilken som passer, avhenger av ett enkelt spørsmål: sitter spilleren inne på din side, eller et helt annet sted?

Ut av Puzzel
Inn i Puzzel

Resultat-webhook

Slå på "Send resultater til en webhook" i editoren og oppgi en URL. Fra da av sender spillerens nettleser hele resultatet som JSON med POST til den URL-en, hver gang fremgangen lagres.

Sette det opp
  1. 1 Åpne aktiviteten i editoren og gå til Utvikler-menyen.
  2. 2 Slå på "Send resultater til en webhook" og lim inn endepunktet ditt i feltet under. Det må være en fullstendig URL — et bart domene avvises — og det må være https, fordi nettleseren blokkerer et vanlig http-kall fra en side som vises over https.
  3. 3 Spill aktiviteten selv én gang. Den første POST-en kommer så snart du svarer på noe.
En mottaker, fra ende til annen
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);
});
Den utløses mens du spiller, ikke bare til slutt

Et resultat sendes hver gang oppføringen lagres: etter en pause i skrivingen, når et kort plasseres, når klokken stopper, og én gang til når aktiviteten er ferdig. Et langt kryssord blir et titalls kall, ikke ett — så skriv behandleren din som en upsert nøkkelbasert på playerUid og activityKey, i stedet for som en innsetting. Hvert kall inneholder hele tilstanden, så det nyeste alltid overstyrer det forrige, og et kall som forsvinner blir tatt igjen av det neste.

Å skille en fullføring fra en lagring

progress er en prosentandel: 100 betyr at aktiviteten er fullført. Noen få typer kan avsluttes uten å nå den — en quiz besvart helt til slutten, et løst løsningsfelt — og disse har hasAlternateCompletion i stedet. Behandle begge som fullført.

Den sendes av spillerens nettleser

POST-en kommer fra fanen aktiviteten spilles i, ikke fra en Puzzel-server. De fleste endepunkter bygget for å motta webhooks godtar den allerede. Hvis ditt aldri ser en forespørsel, er dette hvorfor: nettleseren spør om tillatelse først, så svar på OPTIONS-preflighten med en Access-Control-Allow-Origin-header, og den ekte POST-en følger etter.

Behandle innholdet som et utsagn, ikke et bevis

Forespørselen er ikke signert, og den kommer fra en nettleser du ikke har kontroll over, så alle som ser på siden kan sende deg en også. Det er greit for å fylle ut en fremdriftslinje eller et dashbord. For alt du ikke ville latt en elev sette selv — en karakter som teller, et sertifikat, en betaling — bør du sjekke det mot resultatene i ditt eget Puzzel-dashbord, eller la LMS-karakterkoblingene sende poengsummen i stedet.

Resultater til siden rundt

Hvis du bygger inn aktiviteten, kan du få den samme JSON-en sendt til din egen side i stedet for til en server. Slå på "Send resultater til den overordnede siden" og lytt etter meldingen. Ingenting forlater nettleseren, så det er ikke noe endepunkt å bygge og ingen CORS å tenke på.

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>
Sjekk hvor meldingen kom fra

Lytteren din hører hver melding som sendes til siden, også fra andre rammer og nettleserutvidelser. Sammenlign event.origin med https://puzzel.org før du stoler på innholdet.

Samme innhold, samme tidspunkt

Dette er webhookens tvilling: de samme feltene, sendt på de samme tidspunktene. Alt under "Hva et resultat inneholder" gjelder også her.

Fullføringssignal

Den minste kanalen, for når selve resultatet ikke er din sak: slå på "Send et fullføringssignal" og siden din får én melding i det øyeblikket spilleren er ferdig.

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);
});
Hva som kommer
{
  "completed": true,
  "activityKey": "-Nq8sample_activity_key"
}
Resultatet er allerede lagret når det kommer

Signalet sendes bevisst etter at den fullførende lagringen har landet, så en side som reagerer ved å lese resultatet tilbake, vil finne det der.

Bare inne i en iframe

Begge meldingskanalene sender til siden som rammer inn aktiviteten. Åpnet i sin egen fane er det ingen å si det til, så ingenting sendes.

Hva et resultat inneholder

Én form, uansett hvilken kanal som bærer den. Spillerens svar er nøkkelbasert på aktivitetens egne element-id-er, så de samme nøklene dukker opp i correctUids.

FeltTypeHva det gjør
activityKey
alltid
string
stringAktiviteten resultatet tilhører. Den samme nøkkelen du ser i aktivitetens egen URL, etter ?p=.
playerUid
alltid
string
stringHvem som spilte, som en anonym id. Stabil for denne spilleren på denne enheten, så det er den du nøkkelbaserer resultater på — det er ikke en e-postadresse og ikke en Puzzel-konto.
player
noen ganger
object
objectRegistreringsfeltene aktiviteten ber om, slik du har satt dem opp: name, email, class, student_id og så videre. Fraværende inntil spilleren har registrert seg, og helt fraværende på en aktivitet som ikke ber om noe.
progress
alltid
number
numberHvor langt inne, som en prosentandel. 100 betyr ferdig.
timePassed
alltid
number
numberTid brukt på aktiviteten, i millisekunder.
lastPlayedAt
alltid
number
numberNår dette resultatet ble lagret, som et Unix-tidsstempel i millisekunder.
createdAt
noen ganger
number
numberNår forsøket ble startet, som et Unix-tidsstempel i millisekunder.
playerInput
noen ganger
object
objectHva spilleren faktisk skrev inn, nøkkelbasert på id-en til elementet det tilhører. Formen innenfor avhenger av aktivitetstypen — et ord, en liste over plasserte kort, et valgt alternativ.
correctUids
noen ganger
object
objectHvilke av disse elementene som er riktige, nøkkelbasert på samme måte. Fraværende så lenge ingenting er besvart ennå.
score
noen ganger
number
numberPoeng oppnådd, på typene som gir poeng for en runde. Fraværende overalt ellers — også på en runde som faktisk fikk null poeng, så sjekk at nøkkelen finnes før du leser den.
performance
noen ganger
number
numberTypens eget mål på hvor godt det gikk, der den har et slikt mål — ord per minutt i tastaturtrening, for eksempel.
attempts
noen ganger
number
numberHvilken runde dette er: 1 første gang, én mer for hver gang det startes på nytt. Bare på typer som avslutter en runde tidlig og teller om igjen-forsøkene.
knockedOut
noen ganger
boolean
booleanRunden ble avsluttet på et feil svar og er over uten å være fullført.
hasAlternateCompletion
noen ganger
boolean
booleanAktiviteten ble fullført på en måte som ikke når 100 % — en quiz besvart helt til slutten, et løsningsfelt løst. Behandle det som en fullføring.
missedKeys
noen ganger
array
arrayTegn spilleren stadig fikk feil på, sortert med de vanligste feilene først. Bare i tastaturtrening.
contentVersion
noen ganger
number
numberHvilken versjon av aktivitetens innhold dette ble spilt mot. Den endres når eieren redigerer spørsmålene, så et gammelt resultat kan skilles fra et gjeldende.
En ferdig quiz, slik den kommer inn
{
  "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
}
Fraværende, ikke tomt

Et felt som ikke gjelder, utelates fra JSON-en i stedet for å bli sendt som null eller 0. Det er slik en type som ikke gir poeng for en runde skilles fra en runde som fikk null poeng — så les med en standardverdi og anta aldri at en nøkkel finnes.

Tilbakestille aktiviteten fra siden din

Én instruks går den andre veien. Med "Godta utløsere fra den overordnede siden" slått på kan siden som bygger inn aktiviteten tømme spillerens svar og sette aktiviteten tilbake til start — for din egen "prøv igjen"-knapp utenfor rammen.

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');
Tilbakestilling er den eneste utløseren

Det finnes ingen melding for å sende inn et resultat, åpne avslutningsmeldingen eller hoppe til et spørsmål. En melding som ber om noe annet enn en tilbakestilling, blir ignorert.

Bare siden som bygger inn blir hørt

Utløseren godtas bare fra siden som rammer inn aktiviteten, og ingen andre steder — ikke en søskenramme, ikke et skript på siden. Innstillingen er avslått som standard, så slå den på for aktivitetene du styrer.

Ta med din egen spilleridentitet

Hvis plattformen din allerede vet hvem som spiller, trenger ikke aktiviteten å spørre igjen. Det finnes to håndtrykk, begge for en aktivitet inne på siden din, og begge slås på av oss i stedet for i editoren — de endrer hvem et resultat tilhører, så de settes opp sammen med deg i stedet for fra en avkryssingsboks.

Send oss spilleren

Aktiviteten melder seg med 'app-loaded' og venter. Siden din sender tilbake spillerens opplysninger, og resultatet arkiveres under dem uten at spilleren skriver noe eller ser en registreringsskjerm.

Send oss et token

Samme håndtrykk, men siden din sender JWT-en identitetsleverandøren din har utstedt, i stedet for feltene selv. Vi verifiserer den mot utstederne som er satt opp for kontoen din før spilleren slipper inn, så identiteten er bekreftet i stedet for bare oppgitt — dette er den du bør be om når resultatet må være til å stole på.

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();
});
Be oss om å slå det på

Fortell oss hvilken av de to du vil ha og hvor aktivitetene skal bygges inn, så setter vi opp kontoen din og går gjennom det sammen med deg.

Send oss en e-post om identitet

Opprette aktiviteter fra systemet ditt

Alt over handler om at et resultat kommer ut. Å gå den andre veien — å lage selve aktivitetene fra innhold du allerede har — er Puzzle API-et: én POST per aktivitetstype, og du får tilbake en nøkkel og en URL å bygge inn.

Les API-referansen

Når en ferdig kobling er det bedre svaret

Hvis plattformen på den andre siden er en ekte LMS, trenger du sannsynligvis ikke noe av dette. Karakterer kan gå tilbake til karakterboken av seg selv, uten at du trenger å drifte noe.

Ingen av disse?

Kursplattformer, medlemssider, intranett og alt du har bygget selv er akkurat det kanalene på denne siden er til for. En innbygging pluss fullføringssignalet dekker det meste.

Hva som ikke finnes

Så du slipper å lete etter det:

  • Ingen endepunkt for å lese resultater tilbake. API-et oppretter aktiviteter; resultater forlater gjennom kanalene på denne siden, eller gjennom eksportene i dashbordet ditt.
  • Ingen signatur på webhooken. Det finnes ingenting å verifisere forespørselen mot, og det er derfor et resultat ikke bør være det eneste som står bak noe som betyr noe.
  • Ingen kontoomfattende webhook. URL-en er en innstilling på en aktivitet, så en aktivitet du kopierer tar den med seg, mens en ny starter uten den.
  • Ingenting i et lagspill eller et direkterom. Begge meldingskanalene og webhooken er for solospill, og innstillingene slår seg selv av når lagspill er på.
  • Ingen leveringskø. Ingenting lagres og sendes på nytt — neste lagring er forsøket på nytt, og rundens siste kall er det som teller.

Noe som ikke oppfører seg?

Send forespørselen du prøvde, og feilen du fikk tilbake, så får du et ordentlig svar — fra personen som skrev endepunktet.

E-post til support