Webhook 㧯事件
API 版本 2026-10-affable-towhee
Lingara 會將你帳號个課程計畫㧯用量發生个事記做事件,也會接收你个遊戲抑係應用程式送來个事件。每隻事件,毋管仰般傳送,都用共樣个封包,事件目錄將全部个事件都列出來。
封包
每隻事件都帶等六隻欄位。id 用 lgr_evt_ 開頭,係獨一个,係用來去除重複个鍵。type 講係麼个事件。created_at 係事件發生个時間。api_version 係 data 所用个版本:就係你个用戶端固定个版本;在事件源㧯事件串流,就係你个請求在 Lingara-Version 指定个版本。subject 用 lgr_sub_ 開頭,講這隻事件係關係麼人:對你个用戶端來講佢毋會變,毋過每隻用戶端都無共樣,而且永遠毋係電子郵件地址、名抑係帳號 ID。data 當細,淨講出資源,毋會將資源複製:請用愛用个權限範圍去拿資源。
有兩種類型愛各講一句。lesson_plan.ready 對共一份計畫可能會到兩擺,頭一擺 data.status 係 partial,再來係 complete:你做得照頭一擺拿一份做得用个計畫,抑係等 complete 正拿齊每一組內容。usage.threshold_reached 淨會為按用量計費个帳號㧯用戶端傳送,一擺超過幾下隻門檻个時節,淨會報超過个最高該隻,所以毋好預期每隻門檻都有一隻事件。
一份紀錄,三種聽法
Webhook 適合有公開 HTTPS 端點个伺服器。事件源㧯事件串流適合無公開端點个程式,比如在玩家電腦運作个遊戲。每種方式个封包都共樣,所以程式做得先用事件源,後來正換用 Webhook,讀事件个方法毋使改。事件路由係分原生程式用个。在瀏覽器裡肚運作个遊戲這下還呼叫毋到,因為 /v1/ 毋會回應跨來源預檢請求。
麼人會收到事件
用戶端愛有 events:read 㧯該隻事件類型本身个權限範圍(事件目錄有列),而且事件係關係這隻用戶端个擁有者,佢正會收到這隻事件。在事件源㧯事件串流,存取權杖个權限範圍會再縮狹範圍,types 會將佢縮狹到你指名个類型。webhook.test 淨會送去佢指定个端點,永遠毋會入去事件源,也毋做得訂閱。app.installed 㧯 app.uninstalled 淨會送分應用程式自家个用戶端,永遠毋會送分共一隻帳號个別隻用戶端。
註冊端點
在 Lingara 網頁應用程式个 Webhook 頁面(網址係 app.getlingara.com/admin/webhooks)註冊端點,愛先揀用戶端。端點 URL 一定愛在連接埠 443 用 https,佢个主機淨做得解析到公開个地址。揀愛傳送个事件:淨會列出用戶端个權限範圍允許个類型。URL 㧯愛傳送个事件以後都毋做得改:請加一隻新端點,再將舊个刪除。簽名密鑰用 lgr_whsec_ 開頭,淨顯示一擺。
驗證傳送
每擺傳送都係一隻 POST,照 Standard Webhooks 規格,帶等三隻標頭:webhook-id(事件个 id)、webhook-timestamp 㧯 webhook-signature。HMAC 金鑰係簽名密鑰裡肚 lgr_whsec_ 後背該部分用 base64 解碼个結果,永遠毋係歸隻密鑰字串。請在解析內容之前,用佢原本个位元組驗證,像下背恁樣。時間戳記㧯這下差超過五分鐘个傳送愛拒絕,這係 Standard Webhooks 函式庫个預設值:恁樣做得防止分人攔到个傳送再分人重送。
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 為大部分个語言發佈驗證函式庫。這兜函式庫預期密鑰寫做 whsec_ 加 base64,抑係淨 base64,所以愛將 Lingara 密鑰裡肚 lgr_whsec_ 後背該部分交分佢。Lingara 自家个函式庫接受歸隻密鑰。
緊回應,準備會重試
請在 10 秒肚用任何 2xx 回應,以後正做事。其他任何結果,包括逾時抑係 3xx(毋會跟重新導向),都會用越來越長个間隔重試差毋多一日。對自動傳送回應 410,端點馬上就停用;對測試抑係重新傳送回應 410 就毋會。連續五日傳送失敗以後,端點也會停用。毋管哪種,都會寄電子郵件分佢个擁有者。Lingara 這片个故障永遠毋會算入停用端點。在 Webhook 頁面,你做得傳送測試,抑係重新傳送最近 30 日肚个任何一擺傳送。每擺都淨試一擺,永遠毋會重試,就算端點已經停用也會照送。
傳送最少一擺,而且無保證順序。共一隻事件可能會到兩擺,重試也可能在較慢个事件後背正到。webhook-id 每擺重試都共樣,在 30 日肚个重新傳送也共樣。請將你處理過个每隻 id 記 30 日,重複个就毋使理。假使順序重要,就照 created_at 排。
輪換簽名密鑰
一隻端點做得共時有兩隻簽名密鑰。兩隻都有效个時節,webhook-signature 會帶等兩隻 v1, 項目,接受其中一隻个接收端就做得照常運作。請將新密鑰加入你个伺服器再部署,然後正撤銷舊个。
事件源
用用戶端个存取權杖呼叫 GET /v1/events,會轉 items(封包)、next_cursor 㧯 has_more。權杖愛有 events:read 㧯你想愛收个每種類型个權限範圍:假使淨有 events:read,事件源係空个。佢從這下開始。傳 start=oldest 就會拿到差毋多最近 30 日个事件。佢毋使公開端點,也毋使簽名密鑰:存取權杖就證明係麼人在問。下背个換取要求課程計畫事件愛用个兩隻權限範圍。
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 一定會有:請將佢存起來,再當做 cursor 送轉來。佢係不透明个。has_more 係 true 就係愛馬上再呼叫,係 false 就係你已經跟上了:等一下再輪詢,抑係開事件串流。超過 30 日个游標會分人拒絕,轉 410 㧯 cursor_expired。假使無游標,事件源從這下開始,中間个事件會跳過。想愛將該兜事件拿轉來,就用 start=oldest 呼叫,佢會追到事件保留最早个時間,再跳過你已經處理過个 id 值。
事件串流
GET /v1/events/stream 用伺服器傳送事件个形式送共樣个事件。每隻 event 框个 data 係一隻封包,每隻框个 id: 係一隻游標,㧯 next_cursor 係共一種值,所以你做得在事件源㧯事件串流之間切換,毋會漏忒。連線斷忒、收到 done 框(事件串流有時會自家結束)抑係 error 框以後,請將 Last-Event-ID 設做你收到个最後一隻 id:,再重新連線。大部分个 SSE 用戶端會替你做這步,Lingara 函式庫个 tailEvents 也係(裡肚个 streamEvents 淨係單一連線)。佢係游標,毋係事件个 id。事件串流會送心跳,所以靜靜無聲个連線就係斷忒个連線。
curl -N "https://api.getlingara.com/v1/events/stream" \
-H "Authorization: Bearer $LINGARA_TOKEN"將事件送分 Lingara
用 events:write 呼叫 POST /v1/events,用 {type, data} 个形式將事件送分 Lingara:world.context_changed(一隻 scene、source_lang、target_lang、level,還做得加一隻帶 name 㧯 persona 个 npc,還有 tags)抑係 world.practice_requested(一隻 topic,㧯共樣个語言㧯程度)。Idempotency-Key 係必要个:最多 255 隻看得到个 ASCII 字元,比如 UUID。假使無佢,回應係 400 㧯 idempotency_key_required。每隻事件設一擺,重試个時節送共一隻鍵。一隻鍵就係一隻事件:一日肚,用共一隻鍵个第二隻請求會拿到頭一擺个回應(用 JSON 來看相等,毋係逐隻位元組共樣),就算內容無共樣也係恁樣;以後佢會拿到共一隻事件,像下背恁樣。送入來个事件無簽名:你个存取權杖就係證明。請描寫世界,毋係玩家:scene、npc、topic 抑係 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}}'每隻文字欄位都係一行看得到个字元,去忒頭尾个空白正算:scene 㧯 topic 最多 160,npc.name 最多 32,npc.persona 最多 120。換行、定位字元㧯其他控制字元會分人拒絕,看毋到个字元㧯格式字元也共樣:方向覆寫字元、零寬度字元(某兜文字㧯表情符號愛用个連接字元毋算)、標籤區塊㧯私人使用區字元。tags 最多有 8 隻細寫个機器記號,每隻最多 24 隻字元,永遠毋會入去課程計畫。level 係 1 到 9,兩種語言一定愛無共樣。超過這兜限制个請求會分人拒絕,轉 400,也毋會記任何事件。
用 "generate": true(world.practice_requested 个預設值)个時節,權杖還愛有 lesson_plans:write。假使無佢,請求會分人拒絕,轉 403,毋會記任何事件。假使有佢,Lingara 會開始一份課程計畫,檢查㧯收費都㧯直接建立一份共樣,202 回應裡肚个 reaction 會講發生麼个事。假使係 started 而且 plan_status 係 generating,接下來在你用个每種方式都會收到一隻 lesson_plan.ready 抑係 lesson_plan.failed,佢个 data.plan_id 就係回應裡肚个 plan_id。假使係 partial 抑係 complete,計畫係從計畫庫來个,這下就做得讀,也無保證會有事件:還係可能會到一隻,所以淨有 generating 正值得等。假使係 refused 抑係 failed,事件還係成立。佢毋會用共一隻鍵重試,所以想愛再試就送一隻新事件。
一日肚个重試會拿到頭一擺个回應。以後个重試會從存起來个事件重新組出來,佢保留事件開始个計畫,毋過無保留反應分人拒絕个原因。所以較慢个重試可能會用 reaction failed 㧯 internal 回應:意思係頭一擺个結果無記到,毋係講無計畫。假使你有留 plan_id,就用佢讀計畫,抑係送一隻新事件。
Tidewater Games:無伺服器个遊戲
Tidewater Games 係一間虛構个工作室,用 Godot 在做一隻分玩家去夜市四圍行个遊戲。佢个開發者在自家个電腦、用自家个用戶端運作這隻遊戲。
玩家行入一隻麵攤。遊戲用 "generate": true 送出 world.context_changed(就係頂高「將事件送分 Lingara」該節个指令),再將回應裡肚个 plan_id 留起來。
假使 plan_status 係 generating,遊戲就讀事件串流,抑係輪詢事件源,一直到帶等該隻 plan_id 个 lesson_plan.ready 到為止。然後佢用 lesson_plans:read 讀計畫,像下背恁樣。假使計畫已經係 complete,佢就馬上讀。
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"後來工作室加一台有 HTTPS 端點个細伺服器,再為 lesson_plan.ready 將佢註冊。共一隻事件會帶等共一隻 id 送到該位,遊戲讀事件个程式碼毋使改。
用戶端密鑰永遠毋好㧯遊戲版本共下發佈,因為玩家裝置頂高麼个都讀得到。在 Lingara 支援代替玩家登入之前,在玩家電腦運作个遊戲愛㧯自家个伺服器溝通,淨有開發者自家該份正直接㧯 Lingara 溝通。
事件仰般算錢
㧯每擺 /v1/ 呼叫共樣,你用戶端个用量會算每隻接受个送入事件、每擺事件源呼叫㧯每擺開个事件串流。用 "generate": true 送个事件也會算做一份課程計畫。每擺 Webhook 傳送照每隻事件每隻端點算一擺,在佢頭一擺 2xx 个時節算,毋管該係第幾擺試。以後永遠毋會再算,測試也永遠毋算。GET /v1/usage 會顯示這隻月到這下个用量。
假使翻譯㧯英文參考文件無共樣,就以英文參考文件為準。