Hubungkan Puzzel ke platformmu sendiri
Puzzel bisa mengirim hasil pemain langsung ke sistem lain — skornya, sejauh mana progresnya, apa yang dijawabnya — tanpa sistem itu harus berupa LMS lengkap. Halaman ini memuat semua saluran yang ada: apa yang dikirim, kapan dikirimnya, dan hal paling sederhana yang perlu kamu bangun untuk menangkapnya.
- Hasil dikirim melalui
- Webhook, atau pesan ke halaman di sekitar aktivitas
- Halamanmu bisa
- Memasukkan pemain dan mengatur ulang aktivitas
- Diaktifkan
- Per aktivitas, di menu Pengembang pada editor
- Tersedia di
- Paket berbayar — pengaturan ini nonaktif pada akun gratis
Saluran mana yang kamu butuhkan?
Ada tiga hal yang bisa keluar dari sebuah aktivitas dan satu yang bisa masuk. Mana yang cocok bergantung pada satu pertanyaan: apakah pemain berada di dalam halamanmu, atau di tempat lain sepenuhnya?
Setiap hasil yang tersimpan dikirim dengan POST sebagai JSON ke URL milikmu.
- Gunakan saat
- Pemain bisa berada di mana saja — tautan yang dibagikan, kode QR, situs milik orang lain — dan kamu ingin hasilnya masuk ke database milikmu sendiri.
- Yang kamu butuhkan
- Endpoint HTTPS yang menerima POST lintas origin.
save_puzzle_results_via_webhookJSON yang sama, dikirim ke halaman yang menyematkan aktivitas, bukan ke server.
- Gunakan saat
- Kamu menyematkan aktivitas di halaman kursusmu sendiri dan halaman itu sendiri bisa melakukan sesuatu dengan hasilnya.
- Yang kamu butuhkan
- Iframe di halamanmu dan pendengar pesan. Tidak perlu server, tidak perlu CORS.
save_results_iframe_postmessageSatu pesan saat pemain selesai, tanpa membawa apa pun selain fakta itu.
- Gunakan saat
- Yang ingin kamu ketahui hanya apakah mereka sudah selesai — untuk mencentang pelajaran itu, membuka pelajaran berikutnya, atau menampilkan layarmu sendiri.
- Yang kamu butuhkan
- Iframe di halamanmu dan pendengar pesan.
send_completion_signal_when_embeddedWebhook hasil
Aktifkan "Kirim hasil ke webhook" di editor dan berikan sebuah URL. Sejak saat itu, setiap kali progres pemain disimpan, browser mereka mengirim (POST) seluruh hasil ke URL itu sebagai JSON.
- 1 Buka aktivitas di editor dan masuk ke menu Pengembang.
- 2 Aktifkan "Kirim hasil ke webhook" dan tempel endpoint-mu ke kolom di bawahnya. URL itu harus lengkap — domain saja akan ditolak — dan harus https, karena browser memblokir panggilan http biasa dari halaman yang disajikan lewat https.
- 3 Mainkan aktivitas itu sendiri sekali. POST pertama akan tiba begitu kamu menjawab sesuatu.
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);
});Hasil dikirim setiap kali entri disimpan: setelah jeda saat mengetik, saat kartu ditempatkan, saat waktu berhenti, dan sekali lagi saat aktivitas selesai. Sebuah teka-teki silang yang panjang bisa menghasilkan puluhan panggilan, bukan hanya satu — jadi tulis handler-mu sebagai upsert dengan kunci playerUid dan activityKey, bukan sebagai insert. Setiap panggilan membawa keadaan lengkap, jadi yang terbaru selalu menggantikan yang sebelumnya, dan panggilan yang hilang akan tergantikan oleh panggilan berikutnya.
progress adalah persentase: 100 berarti aktivitas selesai. Beberapa tipe bisa berakhir tanpa mencapai itu — Quiz yang dijawab sampai habis, kolom solusi yang terpecahkan — dan itu ditandai dengan hasAlternateCompletion. Perlakukan keduanya sebagai selesai.
POST ini berasal dari tab tempat aktivitas dimainkan, bukan dari server Puzzel. Sebagian besar endpoint yang dibuat untuk menerima webhook sudah bisa menerimanya. Jika endpoint-mu tidak pernah melihat permintaan apa pun, inilah sebabnya: browser meminta izin terlebih dahulu, jadi jawab preflight OPTIONS dengan header Access-Control-Allow-Origin, dan POST yang sesungguhnya akan menyusul.
Tidak ada tanda tangan pada permintaan ini, dan itu berasal dari browser yang tidak kamu kendalikan, jadi siapa pun yang melihat halaman itu juga bisa mengirimkannya kepadamu. Itu tidak masalah untuk mengisi bilah kemajuan atau dasbor. Untuk apa pun yang tidak ingin kamu biarkan siswa atur sendiri — nilai yang diperhitungkan, sertifikat, pembayaran — periksa itu terhadap hasil di dasbormu sendiri di Puzzel, atau biarkan konektor nilai LMS yang membawa skornya.
Hasil ke halaman di sekitarnya
Jika kamu menyematkan aktivitas ini, kamu bisa membuat JSON yang sama itu dikirim ke halamanmu sendiri, bukan ke server. Aktifkan "Kirim hasil ke halaman induk" dan dengarkan pesannya. Tidak ada yang keluar dari browser, jadi tidak perlu membangun endpoint dan tidak perlu memikirkan CORS.
<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>Pendengar pesanmu menangkap setiap pesan yang dikirim ke halaman, termasuk dari frame lain dan ekstensi browser. Bandingkan event.origin dengan https://puzzel.org sebelum kamu memercayai isinya.
Ini adalah kembaran dari webhook: kolom yang sama, dikirim pada momen yang sama. Semua yang ada di bawah "Apa isi sebuah hasil" juga berlaku di sini.
Sinyal penyelesaian
Saluran paling sederhana, untuk saat hasilnya sendiri bukan urusanmu: aktifkan "Kirim sinyal penyelesaian" dan halamanmu akan menerima satu pesan begitu pemain selesai.
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"
}Sinyal ini sengaja dikirim setelah penyimpanan penyelesaiannya selesai, jadi halaman yang bereaksi dengan membaca kembali hasilnya akan menemukannya di sana.
Kedua saluran pesan mengirim ke halaman yang membingkai aktivitas. Jika dibuka di tabnya sendiri, tidak ada yang bisa diberi tahu, jadi tidak ada yang dikirim.
Apa isi sebuah hasil
Satu bentuk yang sama, saluran apa pun yang membawanya. Jawaban pemain diberi kunci berdasarkan id item milik aktivitas itu sendiri, jadi kunci yang sama muncul juga di correctUids.
| Kolom | Tipe | Fungsinya |
|---|---|---|
activityKey selalu string | string | Aktivitas yang menjadi pemilik hasil ini. Kunci yang sama seperti yang kamu lihat di URL aktivitas itu sendiri, setelah ?p=. |
playerUid selalu string | string | Siapa yang bermain, sebagai id anonim. Tetap sama untuk pemain ini di perangkat ini, jadi inilah yang kamu jadikan kunci untuk hasil — ini bukan alamat email dan bukan akun Puzzel. |
player terkadang object | object | Kolom pendaftaran yang diminta aktivitas, sesuai yang kamu konfigurasikan: name, email, class, student_id, dan seterusnya. Tidak ada sampai pemain mendaftar, dan sama sekali tidak ada pada aktivitas yang tidak meminta apa pun. |
progress selalu number | number | Sejauh mana progresnya, sebagai persentase. 100 berarti selesai. |
timePassed selalu number | number | Waktu yang dihabiskan pada aktivitas, dalam milidetik. |
lastPlayedAt selalu number | number | Kapan hasil ini disimpan, sebagai Unix timestamp dalam milidetik. |
createdAt terkadang number | number | Kapan percobaan ini dimulai, sebagai Unix timestamp dalam milidetik. |
playerInput terkadang object | object | Apa yang benar-benar dimasukkan pemain, diberi kunci berdasarkan id item yang bersangkutan. Bentuk di dalamnya bergantung pada tipe aktivitas — sebuah kata, daftar kartu yang ditempatkan, atau pilihan yang dipilih. |
correctUids terkadang object | object | Item mana saja yang benar, dengan kunci yang sama. Tidak ada selama belum ada yang dijawab. |
score terkadang number | number | Poin yang didapat, pada tipe yang memberi skor untuk satu sesi. Tidak ada di tipe lainnya — termasuk pada sesi yang skornya memang nol, jadi periksa dulu apakah kuncinya ada sebelum membacanya. |
performance terkadang number | number | Ukuran khusus milik suatu tipe tentang seberapa baik hasilnya, jika tipe itu punya ukuran seperti itu — misalnya kata per menit pada latihan mengetik. |
attempts terkadang number | number | Sesi ke berapa ini: 1 untuk yang pertama kali, bertambah satu setiap kali dimulai ulang. Hanya ada pada tipe yang bisa mengakhiri sesi lebih awal dan menghitung jumlah pengulangannya. |
knockedOut terkadang boolean | boolean | Sesi berakhir karena jawaban yang salah dan selesai tanpa menjadi tuntas. |
hasAlternateCompletion terkadang boolean | boolean | Aktivitas diselesaikan dengan cara yang tidak mencapai 100% — Quiz yang dijawab sampai habis, kolom solusi yang terpecahkan. Perlakukan ini sebagai penyelesaian. |
missedKeys terkadang array | array | Karakter yang terus-menerus salah diketik pemain, yang paling sering salah ditampilkan lebih dulu. Hanya pada latihan mengetik. |
contentVersion terkadang number | number | Versi konten aktivitas mana yang dimainkan. Nilainya berubah saat pemilik mengedit pertanyaan, sehingga hasil lama bisa dibedakan dari hasil yang terbaru. |
{
"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
}Kolom yang tidak relevan dihilangkan dari JSON, bukan dikirim sebagai null atau nol. Begitulah cara membedakan tipe yang memang tidak memberi skor pada suatu sesi, dari sesi yang skornya nol — jadi baca dengan nilai default dan jangan pernah menganggap sebuah kunci pasti ada.
Mengatur ulang aktivitas dari halamanmu
Satu instruksi berjalan ke arah sebaliknya. Dengan "Terima pemicu dari halaman induk" diaktifkan, halaman yang melakukan penyematan bisa menghapus jawaban pemain dan mengembalikan aktivitas ke awal — untuk tombol "coba lagi" milikmu sendiri, di luar frame.
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');Tidak ada pesan untuk mengirim hasil, membuka pesan penutup, atau melompat ke pertanyaan tertentu. Pesan yang meminta selain pengaturan ulang akan diabaikan.
Pemicu ini hanya diterima dari halaman yang membingkai aktivitas dan tidak dari tempat lain mana pun — bukan dari frame lain yang sejajar, bukan dari skrip di halaman itu. Pengaturan ini nonaktif secara default, jadi aktifkan untuk aktivitas yang kamu kendalikan.
Membawa identitas pemainmu sendiri
Jika platformmu sudah tahu siapa yang bermain, aktivitas tidak perlu menanyakannya lagi. Ada dua jenis handshake, keduanya untuk aktivitas di dalam halamanmu, dan keduanya diaktifkan oleh kami, bukan dari editor — karena keduanya menentukan hasil itu menjadi milik siapa, jadi pengaturannya dilakukan bersama kami, bukan lewat sebuah centang.
Aktivitas mengumumkan dirinya dengan 'app-loaded' lalu menunggu. Halamanmu mengirimkan kembali detail pemain, dan hasilnya dicatat atas nama mereka tanpa pemain perlu mengetik apa pun atau melihat layar pendaftaran.
Handshake yang sama, tetapi halamanmu mengirimkan JWT yang diterbitkan oleh penyedia identitasmu, bukan kolom-kolomnya sendiri. Kami memverifikasinya terhadap penerbit yang sudah diatur untuk akunmu sebelum pemain diizinkan masuk, sehingga identitasnya terbukti, bukan sekadar diklaim — inilah yang perlu kamu minta saat hasilnya harus benar-benar bisa dipercaya.
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();
});Beri tahu kami mana dari kedua opsi ini yang kamu inginkan dan di mana aktivitas akan disematkan, dan kami akan menyiapkan akunmu serta memandumu langkah demi langkah.
Kirim email tentang identitasMembuat aktivitas dari sistemmu
Semua yang di atas soal hasil yang keluar. Ke arah sebaliknya — membuat aktivitasnya sendiri dari konten yang sudah kamu punya — itulah Puzzle API: satu POST per tipe aktivitas, dan kamu akan mendapatkan kembali sebuah kunci dan URL untuk disematkan.
Baca referensi APISaat konektor siap pakai adalah jawaban yang lebih baik
Jika platform di sisi lain adalah LMS sungguhan, kemungkinan besar kamu tidak memerlukan semua ini. Nilai bisa langsung masuk ke buku nilainya sendiri, tanpa perlu kamu hosting apa pun.
Diluncurkan dari dalam LMS, dengan skor yang dituliskan kembali ke buku nilainya.
Kirim aktivitas sebagai tugas dan nilainya akan otomatis masuk kembali.
Platform kursus, situs keanggotaan, intranet, dan apa pun yang kamu bangun sendiri, justru itulah yang menjadi tujuan saluran-saluran di halaman ini. Sematan ditambah sinyal penyelesaian sudah mencakup sebagian besar kebutuhan itu.
Apa yang tidak ada
Supaya kamu tidak repot mencarinya:
- Tidak ada endpoint untuk membaca kembali hasil. API ini membuat aktivitas; hasil keluar lewat saluran-saluran di halaman ini, atau lewat ekspor di dasbormu.
- Tidak ada tanda tangan pada webhook. Tidak ada yang bisa dijadikan acuan untuk memverifikasi permintaannya, itulah sebabnya sebuah hasil tidak boleh menjadi satu-satunya dasar untuk sesuatu yang penting.
- Tidak ada webhook untuk seluruh akun. URL-nya adalah pengaturan pada satu aktivitas, jadi aktivitas yang kamu salin akan membawanya, sementara aktivitas baru dimulai tanpanya.
- Tidak berlaku pada permainan tim atau ruang langsung. Kedua saluran pesan dan webhook hanya untuk permainan solo, dan pengaturannya otomatis nonaktif saat mode tim diaktifkan.
- Tidak ada antrean pengiriman. Tidak ada yang disimpan lalu dikirim ulang — penyimpanan berikutnya berfungsi sebagai percobaan ulang, dan panggilan terakhir dari sesi itulah yang menjadi penentu.
Ada yang tidak beres?
Kirim permintaan yang kamu coba dan error yang kamu terima, lalu kamu akan mendapat jawaban sungguhan, dari orang yang menulis endpoint itu.
Kirim email ke dukungan