Webhook dan peristiwa
Versi API 2026-10-affable-towhee
Lingara merekodkan perkara yang berlaku pada rancangan pelajaran dan penggunaan akaun anda sebagai peristiwa, dan menerima peristiwa daripada permainan atau aplikasi anda. Setiap peristiwa, melalui mana-mana laluan, mempunyai sampul yang sama, dan katalog peristiwa menyenaraikan semuanya.
Sampul
Setiap peristiwa membawa enam medan. id bermula dengan lgr_evt_, unik, dan merupakan kunci untuk nyahpenduaan. type menamakan peristiwa itu. created_at ialah masa ia berlaku. api_version ialah versi yang membentuk data: versi yang disematkan pada klien anda, atau, pada suapan dan strim, versi yang dinamakan oleh permintaan anda dalam Lingara-Version. subject bermula dengan lgr_sub_ dan menyatakan siapa yang dimaksudkan oleh peristiwa itu: ia stabil untuk klien anda tetapi berbeza bagi setiap klien, dan tidak sekali-kali e-mel, nama atau ID akaun. data adalah kecil dan menamakan sumber dan bukannya menyalinnya: ambil sumber dengan skop yang diperlukannya.
Dua jenis memerlukan satu ayat setiap satu. lesson_plan.ready boleh tiba dua kali untuk satu rancangan, mula-mula dengan data.status partial dan kemudian complete: bertindak pada yang pertama untuk rancangan yang boleh digunakan, atau tunggu complete untuk mendapatkan setiap set. usage.threshold_reached hanya dihantar untuk akaun dan klien bermeter, dan lompatan melepasi beberapa ambang hanya melaporkan ambang tertinggi yang dilepasi, jadi jangan jangka satu peristiwa bagi setiap ambang.
Satu log, tiga cara mendengarnya
Webhook sesuai untuk pelayan yang mempunyai titik akhir HTTPS awam. Suapan dan strim sesuai untuk program yang tidak mempunyainya, seperti permainan pada mesin pemain. Sampulnya sama pada setiap laluan, jadi program boleh bermula dengan suapan dan beralih ke webhook kemudian tanpa mengubah cara ia membaca peristiwa. Laluan peristiwa melayan program asli. Permainan yang berjalan dalam pelayar belum boleh memanggilnya, kerana /v1/ tidak menjawab permintaan preflight rentas asal.
Siapa yang mendengar sesuatu peristiwa
Klien mendengar sesuatu peristiwa apabila ia memegang events:read dan skop jenis peristiwa itu sendiri, yang disenaraikan dalam katalog, dan apabila peristiwa itu berkenaan pemilik klien. Pada suapan dan strim, skop token akses menyempitkannya lagi, dan types menyempitkannya kepada jenis yang anda namakan. webhook.test hanya pergi ke titik akhir yang menjadi destinasinya, tidak sekali-kali ke suapan, dan tidak boleh dilanggan. app.installed dan app.uninstalled hanya pergi ke klien aplikasi itu sendiri, tidak sekali-kali ke klien lain dalam akaun yang sama.
Daftarkan titik akhir
Daftarkan titik akhir pada halaman Webhook dalam aplikasi web Lingara, di app.getlingara.com/admin/webhooks, dengan memilih klien terlebih dahulu. URLnya mesti menggunakan https pada port 443, dan hosnya mesti diselesaikan kepada alamat awam sahaja. Pilih Peristiwa untuk dihantar: hanya jenis yang dibenarkan oleh skop klien ditawarkan. URL dan peristiwa tidak boleh disunting kemudian: tambah titik akhir baharu dan padam yang lama. Rahsia tandatangan bermula dengan lgr_whsec_ dan ditunjukkan sekali sahaja.
Sahkan penghantaran
Setiap penghantaran ialah POST dengan tiga pengepala, mengikut spesifikasi Standard Webhooks: webhook-id (id peristiwa itu), webhook-timestamp dan webhook-signature. Kunci HMAC ialah bahagian rahsia selepas lgr_whsec_ yang dinyahkod base64, tidak sekali-kali rahsia itu sebagai rentetan. Sahkan sebelum menghurai badan, ke atas bait mentahnya, seperti di bawah. Tolak penghantaran yang cap masanya lebih daripada lima minit dari sekarang, nilai lalai pustaka Standard Webhooks: ini menghalang penghantaran yang dipintas daripada dimainkan semula.
signed = webhook-id + "." + webhook-timestamp + "." + raw request body
key = base64_decode(the secret after its prefix)
expected = "v1," + base64(hmac_sha256(key, signed))
accept if |now - webhook-timestamp| <= 5 minutes
and some entry of webhook-signature (space-separated) equals expected
(compare in constant time)Standard Webhooks menerbitkan pengesah untuk kebanyakan bahasa. Pengesah itu menjangkakan rahsia yang ditulis sebagai whsec_ diikuti base64, atau base64 sahaja, jadi berikan bahagian rahsia Lingara selepas lgr_whsec_. Pustaka Lingara sendiri menerima keseluruhan rahsia.
Jawab dengan pantas, jangkakan cubaan semula
Jawab dengan sebarang 2xx dalam masa 10 saat, dan lakukan kerja selepas itu. Jawapan lain, termasuk tamat masa atau 3xx (ubah hala tidak diikuti), dicuba semula dengan jarak yang semakin panjang selama kira-kira sehari. 410 sebagai jawapan kepada penghantaran automatik melumpuhkan titik akhir serta-merta; 410 sebagai jawapan kepada ujian atau penghantaran semula tidak. Selepas lima hari penghantaran gagal, titik akhir juga dilumpuhkan. Walau apa pun, pemiliknya dihantar e-mel. Kerosakan di pihak Lingara tidak sekali-kali dikira untuk melumpuhkan titik akhir. Dari halaman Webhook anda boleh menghantar ujian, atau menghantar semula mana-mana penghantaran dalam 30 hari lepas. Setiap satu ialah satu cubaan, tidak dicuba semula, dan dihantar walaupun ke titik akhir yang dilumpuhkan.
Penghantaran berlaku sekurang-kurangnya sekali dan tidak tertib. Peristiwa yang sama boleh tiba dua kali, dan cubaan semula boleh tiba selepas peristiwa yang lebih baharu. webhook-id sama pada setiap cubaan semula, dan pada penghantaran semula sehingga 30 hari kemudian. Rekodkan setiap id yang telah anda kendalikan selama 30 hari, dan abaikan ulangan. Susun mengikut created_at jika tertib penting.
Putarkan rahsia tandatangan
Titik akhir boleh memegang dua rahsia tandatangan sekali gus. Selagi kedua-duanya aktif, webhook-signature membawa dua entri v1,, dan penerima yang menerima salah satunya terus berfungsi. Tambah rahsia baharu pada pelayan anda, gunakannya, kemudian batalkan yang lama.
Suapan
GET /v1/events dengan token akses klien mengembalikan items (sampul), next_cursor dan has_more. Token memerlukan events:read dan skop setiap jenis yang ingin anda dengar: dengan events:read sahaja, suapan kosong. Ia bermula dari sekarang. Hantar start=oldest untuk peristiwa kira-kira 30 hari lepas. Ia tidak memerlukan titik akhir awam atau rahsia tandatangan: token akses membuktikan siapa yang bertanya. Pertukaran di bawah meminta kedua-dua skop yang diperlukan oleh peristiwa rancangan pelajaran.
export LINGARA_TOKEN="$(curl -sS --fail-with-body -X POST "https://api.getlingara.com/oauth/token" \
-u "$LINGARA_CLIENT_ID:$LINGARA_CLIENT_SECRET" \
-d "grant_type=client_credentials" \
--data-urlencode "scope=events:read lesson_plans:read" | jq -r '.access_token // error(.error)')"curl "https://api.getlingara.com/v1/events" \
-H "Authorization: Bearer $LINGARA_TOKEN"next_cursor sentiasa ada: simpan dan hantarnya semula sebagai cursor. Ia legap. has_more true bermaksud panggil semula sekarang, dan false bermaksud anda sudah terkini: tinjau kemudian, atau buka strim. Kursor yang lebih lama daripada 30 hari ditolak dengan 410 dan cursor_expired. Tanpa kursor, suapan bermula dari sekarang, dan peristiwa di antaranya dilangkau. Untuk mendapatkannya semula, panggil dengan start=oldest, yang mencapai sejauh mana peristiwa disimpan, dan langkau nilai id yang telah anda kendalikan.
Strim
GET /v1/events/stream membawa peristiwa yang sama sebagai server-sent events. data setiap bingkai event ialah satu sampul, dan id: setiap bingkai ialah kursor, token yang sama seperti next_cursor, jadi anda boleh bertukar antara suapan dan strim tanpa jurang. Selepas sambungan terputus, bingkai done (strim menamatkan dirinya dari semasa ke semasa) atau bingkai error, sambung semula dengan Last-Event-ID ditetapkan kepada id: terakhir yang anda terima. Kebanyakan klien SSE melakukan ini untuk anda, begitu juga tailEvents dalam pustaka Lingara (streamEvents di sana ialah satu sambungan tunggal). Ia ialah kursor, bukan id peristiwa. Strim menghantar degupan jantung, jadi sambungan yang senyap ialah sambungan yang mati.
curl -N "https://api.getlingara.com/v1/events/stream" \
-H "Authorization: Bearer $LINGARA_TOKEN"Hantar peristiwa kepada Lingara
POST /v1/events dengan events:write menghantar peristiwa kepada Lingara sebagai {type, data}: world.context_changed (scene, source_lang, target_lang, level, dan secara pilihan npc dengan name dan persona, serta tags) atau world.practice_requested (topic serta bahasa dan tahap yang sama). Idempotency-Key diwajibkan: sehingga 255 aksara ASCII yang boleh dilihat, seperti UUID. Tanpanya jawapannya ialah 400 dan idempotency_key_required. Tetapkannya sekali bagi setiap peristiwa, dan hantar kunci yang sama semasa cubaan semula. Satu kunci ialah satu peristiwa: dalam tempoh sehari, permintaan kedua dengan kunci yang sama mendapat jawapan pertama (sama sebagai JSON, bukan bait demi bait), walaupun badannya berbeza, dan selepas itu ia mendapat peristiwa yang sama, seperti di bawah. Peristiwa masuk tidak ditandatangani: token akses anda ialah buktinya. Gambarkan dunia, bukan pemain: tiada nama atau sembang dalam scene, npc, topic atau tags.
export LINGARA_TOKEN="$(curl -sS --fail-with-body -X POST "https://api.getlingara.com/oauth/token" \
-u "$LINGARA_CLIENT_ID:$LINGARA_CLIENT_SECRET" \
-d "grant_type=client_credentials" \
--data-urlencode "scope=events:write lesson_plans:write" | jq -r '.access_token // error(.error)')"curl -X POST "https://api.getlingara.com/v1/events" \
-H "Authorization: Bearer $LINGARA_TOKEN" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"world.context_changed","data":{"scene":"A night market in Taipei, just after rain","npc":{"name":"Auntie Lin","persona":"a street-food vendor who likes to haggle"},"source_lang":"en","target_lang":"zh","level":3,"tags":["market","food","chapter-2"],"generate":true}}'Setiap medan teks ialah satu baris aksara yang boleh dilihat, dikira selepas dipangkas: scene dan topic sehingga 160, npc.name sehingga 32, dan npc.persona sehingga 120. Pemisah baris, tab dan aksara kawalan lain ditolak, begitu juga aksara halimunan dan aksara pemformatan: penindih arah, aksara lebar sifar selain penyambung yang diperlukan oleh sesetengah tulisan dan emoji, blok tag dan aksara kegunaan peribadi. tags memegang sehingga 8 token mesin huruf kecil, masing-masing sehingga 24 aksara, dan tidak sekali-kali sampai ke rancangan pelajaran. level ialah 1 hingga 9, dan kedua-dua bahasa mesti berbeza. Permintaan di luar had ini ditolak dengan 400 dan tidak merekodkan peristiwa.
Dengan "generate": true (lalai untuk world.practice_requested), token juga memerlukan lesson_plans:write. Tanpanya permintaan ditolak dengan 403 dan tiada peristiwa direkodkan. Dengannya, Lingara memulakan rancangan pelajaran, dengan semakan dan bil yang sama seperti menciptanya secara terus, dan reaction dalam jawapan 202 menyatakan apa yang berlaku. Dengan started dan plan_status generating, lesson_plan.ready atau lesson_plan.failed yang data.plan_idnya ialah plan_id jawapan itu akan menyusul, pada setiap laluan yang anda gunakan. Dengan partial atau complete, rancangan itu datang daripada pustaka dan boleh dibaca sekarang, dan tiada peristiwa dijanjikan: satu mungkin masih tiba, jadi hanya generating yang berbaloi ditunggu. Dengan refused atau failed, peristiwa itu tetap kekal. Ia tidak dicuba semula di bawah kunci yang sama, jadi hantar peristiwa baharu untuk mencuba lagi.
Cubaan semula dalam tempoh sehari mendapat jawapan pertama kembali. Cubaan semula selepas itu dibina semula daripada peristiwa yang disimpan, yang mengekalkan rancangan yang dimulakannya tetapi bukan sebab sesuatu reaksi ditolak. Jadi cubaan semula yang lewat boleh menjawab dengan reaction failed dan internal: itu bermaksud hasil pertama tidak direkodkan, bukan bahawa tiada rancangan wujud. Baca rancangan itu melalui plan_idnya jika anda menyimpannya, atau hantar peristiwa baharu.
Tidewater Games: permainan tanpa pelayan
Tidewater Games, sebuah studio rekaan, membina permainan Godot di mana pemain meneroka pasar malam. Pembangunnya menjalankan permainan itu pada mesin mereka sendiri, dengan klien mereka sendiri.
Pemain berjalan masuk ke gerai mi. Permainan menghantar world.context_changed dengan "generate": true, iaitu arahan dalam bahagian “Hantar peristiwa kepada Lingara” di atas, dan menyimpan plan_id daripada jawapan.
Jika plan_status ialah generating, permainan membaca strim, atau meninjau suapan, sehingga lesson_plan.ready dengan plan_id itu tiba. Kemudian ia membaca rancangan itu dengan lesson_plans:read, seperti di bawah. Jika rancangan itu sudah complete, ia membacanya serta-merta.
export LINGARA_TOKEN="$(curl -sS --fail-with-body -X POST "https://api.getlingara.com/oauth/token" \
-u "$LINGARA_CLIENT_ID:$LINGARA_CLIENT_SECRET" \
-d "grant_type=client_credentials" \
--data-urlencode "scope=lesson_plans:read" | jq -r '.access_token // error(.error)')"curl "https://api.getlingara.com/v1/lesson-plans/$ID" \
-H "Authorization: Bearer $LINGARA_TOKEN"Kemudian studio itu menambah pelayan kecil dengan titik akhir HTTPS dan mendaftarkannya untuk lesson_plan.ready. Peristiwa yang sama tiba di sana, dengan id yang sama, dan kod permainan untuk membacanya tidak berubah.
Rahsia klien tidak boleh sekali-kali disertakan di dalam binaan permainan, kerana apa-apa sahaja pada peranti pemain boleh dibaca. Sehingga Lingara menyokong log masuk bagi pihak pemain, permainan pada mesin pemain berkomunikasi dengan pelayan studio itu sendiri, dan hanya salinan pembangun sendiri yang berkomunikasi terus dengan Lingara.
Kos peristiwa
Penggunaan klien anda mengira setiap peristiwa masuk yang diterima, setiap panggilan suapan dan setiap strim yang dibuka, sebagaimana ia mengira setiap panggilan /v1/. Peristiwa yang dihantar dengan "generate": true juga dikira sebagai rancangan pelajaran. Setiap penghantaran webhook dikira sekali bagi setiap peristiwa bagi setiap titik akhir, pada 2xx pertamanya, tidak kira cubaan yang keberapa. Ia tidak dikira lagi, dan ujian tidak sekali-kali dikira. GET /v1/usage menunjukkan penggunaan bulan ini setakat ini.
Jika terjemahan dan rujukan bahasa Inggeris berbeza, rujukan bahasa Inggeris adalah yang betul.