Webhookit ja tapahtumat
API-versio 2026-10-affable-towhee
Lingara kirjaa tilisi tuntisuunnitelmissa ja käytössä tapahtuvat asiat tapahtumiksi ja ottaa vastaan tapahtumia pelistäsi tai sovelluksestasi. Jokaisella tapahtumalla on sama kirjekuori kulkutavasta riippumatta, ja tapahtumaluettelo luettelee ne kaikki.
Kirjekuori
Jokaisessa tapahtumassa on kuusi kenttää. id alkaa lgr_evt_, on yksilöllinen ja on avain, jonka perusteella kaksoiskappaleet poistetaan. type nimeää tapahtuman. created_at kertoo, milloin se tapahtui. api_version on versio, jonka muotoinen data on: asiakkaasi kiinnitetty versio tai, syötteessä ja virrassa, versio, jonka pyyntösi nimesi Lingara-Version-otsakkeessa. subject alkaa lgr_sub_ ja kertoo, ketä tapahtuma koskee: se pysyy samana asiakkaallesi mutta on eri jokaiselle asiakkaalle, eikä se ole koskaan sähköpostiosoite, nimi tai tilin tunniste. data on pieni ja nimeää resursseja kopioimatta niitä: hae resurssi sen vaatimalla oikeuslaajuudella.
Kaksi tyyppiä vaatii kumpikin oman virkkeensä. lesson_plan.ready voi saapua samasta suunnitelmasta kahdesti, ensin niin, että data.status on partial, ja sitten complete: toimi ensimmäisen perusteella saadaksesi käyttökelpoisen suunnitelman tai odota arvoa complete saadaksesi jokaisen sarjan. usage.threshold_reached lähetetään vain käytön mukaan laskutettaville tileille ja asiakkaille, ja jos useampi raja ylittyy kerralla, ilmoitetaan vain korkein ylitetty raja, joten älä odota yhtä tapahtumaa rajaa kohden.
Yksi loki, kolme tapaa kuulla se
Webhookit sopivat palvelimelle, jolla on julkinen HTTPS-päätepiste. Syöte ja virta sopivat ohjelmalle, jolla sellaista ei ole, kuten pelaajan koneella toimivalle pelille. Kirjekuori on sama joka kulkutavalla, joten ohjelma voi aloittaa syötteellä ja siirtyä myöhemmin webhookeihin muuttamatta tapaa, jolla se lukee tapahtuman. Tapahtumareitit vastaavat natiiveille ohjelmille. Selaimessa toimiva peli ei voi vielä kutsua niitä, koska /v1/ ei vastaa yhteenkään cross-origin-esitarkistukseen.
Kuka kuulee tapahtuman
Asiakas kuulee tapahtuman, kun sillä on events:read ja tapahtumatyypin oma oikeuslaajuus, jonka luettelo kertoo, ja kun tapahtuma koskee asiakkaan omistajaa. Syötteessä ja virrassa käyttöoikeustunnuksen oikeuslaajuudet rajaavat tätä lisää, ja types rajaa sen nimeämiisi tyyppeihin. webhook.test menee vain päätepisteeseen, johon se lähetettiin, ei koskaan syötteeseen, eikä sitä voi tilata. app.installed ja app.uninstalled menevät vain sovelluksen omalle asiakkaalle, eivät koskaan saman tilin toiselle asiakkaalle.
Rekisteröi päätepiste
Rekisteröi päätepiste Lingara-verkkosovelluksen Webhookit-sivulla osoitteessa app.getlingara.com/admin/webhooks valitsemalla ensin asiakas. Sen URL-osoitteen on käytettävä https-protokollaa portissa 443, ja sen isäntänimen on ratkettava vain julkisiin osoitteisiin. Valitse ”Lähetettävät tapahtumat”: tarjolla ovat vain tyypit, jotka asiakkaan oikeuslaajuudet sallivat. URL-osoitetta ja tapahtumia ei voi muokata jälkikäteen: lisää uusi päätepiste ja poista vanha. Allekirjoitussalaisuus alkaa lgr_whsec_ ja näytetään vain kerran.
Varmenna toimitus
Jokainen toimitus on POST, jossa on kolme otsaketta Standard Webhooks -määrityksen mukaisesti: webhook-id (tapahtuman id), webhook-timestamp ja webhook-signature. HMAC-avain on salaisuuden lgr_whsec_-etuliitteen jälkeinen osa base64-dekoodattuna, ei koskaan salaisuus merkkijonona. Varmenna ennen rungon jäsentämistä, sen raakatavuista, alla olevan mukaisesti. Hylkää toimitus, jonka aikaleima poikkeaa nykyhetkestä yli viisi minuuttia, mikä on Standard Webhooks -kirjastojen oletus: se estää kaapatun toimituksen uudelleentoiston.
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 julkaisee varmentimia useimmille kielille. Ne odottavat salaisuutta muodossa whsec_ ja sen perässä base64 tai pelkkänä base64:nä, joten anna niille Lingara-salaisuuden lgr_whsec_-etuliitteen jälkeinen osa. Lingaran omat kirjastot hyväksyvät koko salaisuuden.
Vastaa nopeasti, varaudu uudelleenyrityksiin
Vastaa millä tahansa 2xx-koodilla 10 sekunnin kuluessa ja tee työ vasta sen jälkeen. Kaikkea muuta, myös aikakatkaisua tai 3xx-vastausta (uudelleenohjauksia ei seurata), yritetään uudelleen kasvavin välein noin vuorokauden ajan. 410 vastauksena automaattiseen toimitukseen poistaa päätepisteen käytöstä heti; 410 vastauksena testiin tai uudelleentoimitukseen ei poista. Viiden päivän epäonnistuneiden toimitusten jälkeen päätepiste poistetaan myös käytöstä. Kummassakin tapauksessa sen omistajalle lähetetään sähköpostia. Lingaran päässä sattunutta vikaa ei koskaan lasketa päätepisteen käytöstä poistamiseen. Webhookit-sivulla voit valita ”Lähetä testi” tai ”Toimita uudelleen” minkä tahansa viimeisen 30 päivän toimituksen. Kumpikin on yksi yritys, jota ei koskaan yritetä uudelleen, ja se lähetetään myös käytöstä poistettuun päätepisteeseen.
Toimitus tapahtuu vähintään kerran ja ilman järjestystä. Sama tapahtuma voi saapua kahdesti, ja uudelleenyritys voi saapua myöhemmän tapahtuman jälkeen. webhook-id on sama jokaisessa uudelleenyrityksessä ja uudelleentoimituksessa jopa 30 päivää myöhemmin. Kirjaa jokainen käsittelemäsi id 30 päivän ajaksi ja ohita toisto. Järjestä created_at-arvon mukaan, jos järjestyksellä on väliä.
Kierrätä allekirjoitussalaisuus
Päätepisteellä voi olla kaksi allekirjoitussalaisuutta kerrallaan. Kun molemmat ovat voimassa, webhook-signature sisältää kaksi v1,-merkintää, ja vastaanottaja, joka hyväksyy kumman tahansa, toimii edelleen. Lisää uusi salaisuus palvelimellesi, ota se käyttöön ja peruuta sitten vanha.
Syöte
GET /v1/events asiakkaan käyttöoikeustunnuksella palauttaa items (kirjekuoret), next_cursor ja has_more. Tunnus tarvitsee events:read-oikeuslaajuuden ja jokaisen kuultavaksi haluamasi tyypin oikeuslaajuuden: pelkällä events:read-oikeuslaajuudella syöte on tyhjä. Se alkaa nykyhetkestä. Anna start=oldest, niin saat noin viimeisten 30 päivän tapahtumat. Se ei tarvitse julkista päätepistettä eikä allekirjoitussalaisuutta: käyttöoikeustunnus todistaa, kuka kysyy. Alla oleva vaihto pyytää molempia oikeuslaajuuksia, joita tuntisuunnitelmatapahtumat tarvitsevat.
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 on aina mukana: tallenna se ja anna se takaisin parametrina cursor. Se on läpinäkymätön. Kun has_more on true, kutsu heti uudelleen, ja kun se on false, olet ajan tasalla: kysele myöhemmin tai avaa virta. Yli 30 päivää vanha kursori hylätään tilakoodilla 410 ja koodilla cursor_expired. Ilman kursoria syöte alkaa nykyhetkestä, ja väliin jääneet tapahtumat ohitetaan. Saat ne takaisin kutsumalla parametrilla start=oldest, joka ulottuu niin kauas taaksepäin kuin tapahtumia säilytetään, ja ohittamalla jo käsittelemäsi id-arvot.
Virta
GET /v1/events/stream välittää samat tapahtumat server-sent events -muodossa. Jokaisen event-kehyksen data on yksi kirjekuori, ja jokaisen kehyksen id: on kursori, sama tunniste kuin next_cursor, joten voit vaihtaa syötteen ja virran välillä ilman aukkoa. Katkenneen yhteyden, done-kehyksen (virta päättää itsensä ajoittain) tai error-kehyksen jälkeen yhdistä uudelleen asettamalla Last-Event-ID viimeisimpään saamaasi id:-arvoon. Useimmat SSE-asiakkaat tekevät tämän puolestasi, ja niin tekee myös tailEvents Lingaran kirjastoissa (streamEvents on niissä yksittäinen yhteys). Se on kursori, ei tapahtuman id. Virta lähettää sykesignaalia, joten hiljainen yhteys on kuollut.
curl -N "https://api.getlingara.com/v1/events/stream" \
-H "Authorization: Bearer $LINGARA_TOKEN"Lähetä tapahtuma Lingaraan
POST /v1/events oikeuslaajuudella events:write lähettää Lingaraan tapahtuman muodossa {type, data}: world.context_changed (scene, source_lang, target_lang, level ja valinnaisesti npc, jolla on name ja persona, sekä tags) tai world.practice_requested (topic sekä samat kielet ja taso). Idempotency-Key on pakollinen: enintään 255 näkyvää ASCII-merkkiä, esimerkiksi UUID. Ilman sitä vastaus on 400 ja idempotency_key_required. Aseta se kerran tapahtumaa kohden ja lähetä sama avain uudelleenyrityksessä. Yksi avain on yksi tapahtuma: vuorokauden sisällä toinen pyyntö samalla avaimella saa ensimmäisen vastauksen (samana JSONina, ei tavu tavulta), vaikka sen runko olisi eri, ja sen jälkeen se saa saman tapahtuman alla olevan mukaisesti. Saapuvia tapahtumia ei allekirjoiteta: käyttöoikeustunnuksesi on todiste. Kuvaa maailmaa, älä koskaan pelaajaa: ei nimiä tai keskusteluja kenttiin scene, npc, topic tai 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}}'Jokainen tekstikenttä on yksi rivi näkyviä merkkejä, laskettuna reunojen tyhjien poiston jälkeen: scene ja topic enintään 160, npc.name enintään 32 ja npc.persona enintään 120. Rivinvaihdot, sarkaimet ja muut ohjausmerkit hylätään, samoin näkymättömät ja muotoilumerkit: suunnanohitukset, nollaleveät merkit lukuun ottamatta liitosmerkkejä, joita jotkin kirjoitusjärjestelmät ja emojit tarvitsevat, tag-lohko ja yksityiskäyttöön varatut merkit. tags sisältää enintään 8 pienaakkosin kirjoitettua konetunnistetta, kukin enintään 24 merkkiä, eikä se koskaan päädy tuntisuunnitelmaan. level on 1–9, ja kahden kielen on oltava eri. Näiden rajojen ulkopuolinen pyyntö hylätään tilakoodilla 400, eikä tapahtumaa kirjata.
Kun mukana on "generate": true (oletus tapahtumalle world.practice_requested), tunnus tarvitsee myös oikeuslaajuuden lesson_plans:write. Ilman sitä pyyntö hylätään tilakoodilla 403, eikä tapahtumaa kirjata. Sen kanssa Lingara aloittaa tuntisuunnitelman samoin tarkistuksin ja samalla laskutuksella kuin suoraan luotaessa, ja 202-vastauksen reaction kertoo, mitä tapahtui. Kun arvo on started ja plan_status on generating, seuraa lesson_plan.ready tai lesson_plan.failed, jonka data.plan_id on vastauksen plan_id, jokaisella käyttämälläsi kulkutavalla. Arvoilla partial tai complete suunnitelma tuli kirjastosta ja on luettavissa heti, eikä tapahtumaa luvata: sellainen voi silti saapua, joten vain generating on odottamisen arvoinen. Arvoilla refused tai failed tapahtuma pysyy silti voimassa. Sitä ei yritetä uudelleen samalla avaimella, joten lähetä uusi tapahtuma yrittääksesi uudelleen.
Vuorokauden sisällä tehty uudelleenyritys saa ensimmäisen vastauksen takaisin. Sen jälkeen tehty uudelleenyritys rakennetaan uudelleen tallennetusta tapahtumasta, joka säilyttää aloittamansa suunnitelman mutta ei syytä siihen, miksi reaktio hylättiin. Myöhäinen uudelleenyritys voi siis vastata arvoilla reaction failed ja internal: se tarkoittaa, että ensimmäistä lopputulosta ei kirjattu, ei sitä, ettei suunnitelmaa olisi. Lue suunnitelma sen plan_id-tunnisteella, jos säilytit sen, tai lähetä uusi tapahtuma.
Tidewater Games: peli ilman palvelinta
Tidewater Games, kuvitteellinen studio, tekee Godot-peliä, jossa pelaaja tutkii yötoria. Sen kehittäjä ajaa peliä omalla koneellaan omalla asiakkaallaan.
Pelaaja astuu nuudelikojuun. Peli lähettää world.context_changed-tapahtuman arvolla "generate": true, yllä olevan saapuvia tapahtumia käsittelevän osion komennolla, ja säilyttää vastauksen plan_id-arvon.
Jos plan_status on generating, peli lukee virtaa tai kyselee syötettä, kunnes saapuu lesson_plan.ready, jolla on tuo plan_id. Sitten se lukee suunnitelman oikeuslaajuudella lesson_plans:read alla olevan mukaisesti. Jos suunnitelma oli jo complete, se lukee sen heti.
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"Myöhemmin studio lisää pienen palvelimen, jolla on HTTPS-päätepiste, ja rekisteröi sen tapahtumalle lesson_plan.ready. Sama tapahtuma saapuu sinne samalla id-arvolla, eikä pelin koodi sen lukemiseen muutu.
Asiakkaan salaisuutta ei saa koskaan toimittaa pelin koontiversion mukana, koska kaiken pelaajan laitteella olevan voi lukea. Kunnes Lingara tukee sisäänkirjautumista pelaajan puolesta, pelaajien koneilla toimiva peli keskustelee oman palvelimensa kanssa, ja vain kehittäjän oma kopio keskustelee suoraan Lingaran kanssa.
Mitä tapahtumat maksavat
Asiakkaasi käyttöön lasketaan jokainen hyväksytty saapuva tapahtuma, jokainen syötekutsu ja jokainen avattu virta, samoin kuin jokainen /v1/-kutsu. Tapahtuma, joka lähetetään arvolla "generate": true, lasketaan myös tuntisuunnitelmaksi. Jokainen webhook-toimitus lasketaan kerran tapahtumaa ja päätepistettä kohden sen ensimmäisestä 2xx-vastauksesta, olipa se mikä yritys tahansa. Sitä ei koskaan lasketa uudelleen, eikä testiä lasketa koskaan. GET /v1/usage näyttää kuluvan kuukauden tähänastisen käytön.
Jos käännös ja englanninkielinen viite poikkeavat toisistaan, englanninkielinen viite on oikea.