WEBHOOKS
Webhooks
You do not poll for new recordings. When one finishes processing, we POST a signed event to the
URL you registered, and you pull the text with the capability token. The main event is
recording.ready; card and processing events are listed under All events.
不需要輪詢新錄音。一條錄音處理完成後,我們會向你登記的地址 POST 一個帶簽名的事件,你再用能力令牌去取正文。
最主要的事件是 recording.ready;卡片與處理事件見全部事件。
recording.ready
{
"event": "recording.ready",
"recordingId": "rec_01J9…",
"title": "Thursday standup",
"userId": "7f3c…",
"ownerVerified": true,
"svVerdict": "owner",
"timestamp": 1761801600
}
| Field | Meaning |
|---|---|
event | The event name; see all events. Branch on it. |
recordingId | Pass to GET /open/ext/recordings/{id}. Also your idempotency key. |
title | The generated title, or an empty string. |
userId | Which of your authorizing users this belongs to. Match it to your own records. |
ownerVerified | true when the cloud voiceprint review returned owner. |
svVerdict | The raw verdict string, or an empty string if there was none. |
timestamp | Unix seconds at dispatch. |
| 欄位 | 含義 |
|---|---|
event | 事件名,見全部事件。請按它分支。 |
recordingId | 傳給 GET /open/ext/recordings/{id},同時也是你的幂等鍵。 |
title | 生成的標題,或空字串。 |
userId | 這條錄音屬於你哪一位授權用戶。與你自己的記錄對應。 |
ownerVerified | 雲端聲紋複核判為 owner 時為 true。 |
svVerdict | 原始判決字串;沒有則為空字串。 |
timestamp | 投遞時刻的 Unix 秒。 |
There is no transcript text or summary in the payload, by design — the body travels to whatever URL you configured, so it stays metadata. Fetch the content over the API with the capability token, which re-checks the grant on every call.
載荷裡刻意不含轉寫正文與摘要——這個請求體會發往你配置的任意地址,所以只放元數據。 正文請用能力令牌經 API 取回,那條路徑每次調用都會重新校驗授權。
When it fires
All of these have to hold. Any one of them failing means no delivery, silently:
以下條件必須同時成立。任一不成立即不投遞,且不報錯:
- The recording's
visibilityisvisible. Hidden and rejected recordings are never pushed — this is a privacy gate, not a filter you can opt out of. - That user has a live, un-revoked grant to your app that includes
recordings.read. - Your app is not
banned. - Your app has a webhook URL configured.
- If your app is still
sandbox, the recording belongs to your own account. Sandbox apps never receive other people's recordings.
- 該錄音的
visibility為visible。隱藏與被拒的錄音永不推送——這是隱私閘門,不是可以關掉的過濾器。 - 該用戶對你的應用有未撤銷的有效授權,且包含
recordings.read。 - 你的應用不處於
banned狀態。 - 你的應用已配置 webhook 地址。
- 若應用仍是
sandbox態,該錄音屬於你自己的帳號。sandbox 應用永遠收不到他人的錄音。
Verify the signature
X-Vyslo-Signature is the lowercase hex HMAC-SHA256 of the raw request body
keyed with your app's webhookSecret. Verify before you parse, and compare in constant
time. Re-serializing the JSON first will not produce the same digest.
X-Vyslo-Signature 是用應用的 webhookSecret 對原始請求體
做 HMAC-SHA256 後的小寫 hex。先校驗再解析,並用恆定時間比較。先把 JSON 重新序列化會算出不同的摘要。
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["VYSLO_WEBHOOK_SECRET"].encode()
@app.post("/hooks/vyslo")
def vyslo_hook():
raw = request.get_data() # bytes, exactly as received
expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get("X-Vyslo-Signature", "")):
abort(401)
event = request.get_json()
enqueue(event["recordingId"]) # idempotent by recordingId
return "", 204 # ack first, work after
val ok = VysloOpen.verifyWebhookSignature(
webhookSecret = secret,
rawBody = body,
signature = header("X-Vyslo-Signature"),
)
Delivery and retries
| Behaviour | Value |
|---|---|
| Attempts | 3 — the first, then two retries |
| Backoff | 2s before the second attempt, 8s before the third |
| Timeouts | 5s to connect, 10s per attempt |
| Success | Any 2xx. The body is discarded |
| Retried | Any non-2xx, a timeout or a connection error |
| After three failures | The delivery is marked failed and recorded for audit. It is not retried later |
| Ordering | Not guaranteed. Two recordings finishing together may arrive in either order |
| 行為 | 取值 |
|---|---|
| 投遞次數 | 3 次——首次加兩次重試 |
| 退避 | 第二次前等 2 秒,第三次前等 8 秒 |
| 超時 | 連接 5 秒,單次請求 10 秒 |
| 成功 | 任何 2xx。回應體被丟棄 |
| 重試條件 | 非 2xx、超時或連接錯誤 |
| 三次都失敗 | 標記為失敗並落審計記錄,之後不再補投 |
| 順序 | 不保證。同時完成的兩條錄音可能以任意順序到達 |
What your endpoint should do
- Answer fast, work later. Ten seconds is the budget for the whole attempt, so acknowledge and queue rather than transcribing inline.
- Be idempotent on
recordingId. A slow2xxthat we timed out on is still a successful run on your side — you will see that id again. - Verify, then trust. An unverified body is an unauthenticated POST from the open internet.
- Serve HTTPS.
http://is accepted by the API for local development, but the payload names your users.
- 先應答,後處理。單次嘗試總預算 10 秒,請先 ack 入隊,不要在請求裡同步幹活。
- 按
recordingId幂等。我們這邊超時的那次慢2xx,在你那邊仍是一次成功執行——同一個 id 會再來一次。 - 先校驗,再信任。未校驗的請求體,等於來自公網的未鑑權 POST。
- 用 HTTPS。API 允許
http://供本機開發,但載荷裡有你用戶的信息。
All events
| Event | When | Scope |
|---|---|---|
recording.ready | Transcript and summary are ready | recordings.read |
transcript.ready | The transcript is ready, before the summary | events.subscribe |
summary.ready | The summary is ready | events.subscribe |
recording.started | A recording started: button, app or smart wake | events.subscribe |
recording.stopped | A recording stopped, with its origin | events.subscribe |
smartwake.triggered | Smart wake heard the owner and started recording | events.subscribe |
listening.started | Smart wake started listening | events.subscribe |
listening.ended | Smart wake stopped listening | events.subscribe |
button.pressed | A press on the card button | events.subscribe |
charging.changed | The card started or stopped charging | events.subscribe |
| 事件 | 何時 | 權限 |
|---|---|---|
recording.ready | 轉寫與摘要已就緒 | recordings.read |
transcript.ready | 轉寫已就緒,摘要尚在生成 | events.subscribe |
summary.ready | 摘要已就緒 | events.subscribe |
recording.started | 錄音開始:按鍵、App 或智能喚醒發起 | events.subscribe |
recording.stopped | 錄音停止,附停止原因 | events.subscribe |
smartwake.triggered | 智能喚醒聽到機主並開始錄音 | events.subscribe |
listening.started | 智能喚醒開始監聽 | events.subscribe |
listening.ended | 智能喚醒結束監聽 | events.subscribe |
button.pressed | 卡片按鍵被按下 | events.subscribe |
charging.changed | 卡片開始或停止充電 | events.subscribe |
Every event uses the same envelope, signature and retry schedule as recording.ready,
with deviceId added for card events. No payload carries transcript text. Card events
reach the cloud through the phone the card is connected to; over BLE
your app receives them directly.
所有事件與 recording.ready 使用同樣的信封、簽名與重試節奏,卡片事件另帶 deviceId。
任何載荷都不含轉寫正文。卡片事件經卡片所連的手機送到雲端;經 BLE 時,你的 App 直接收到。