Tài liệu dành cho nhà phát triển
Webhooks
Webhook là thông báo có chữ ký cho biết ghi chú đã thay đổi. Hãy lấy nội dung mới nhất qua REST API.
Sự kiện webhook
| Sự kiện | Kích hoạt khi |
|---|---|
| note.ended | Một phiên ghi âm đã kết thúc và có thể lấy được bản chép lời cuối cùng. Kích hoạt một lần cho mỗi phiên ghi âm — ghi chú có nhiều phiên sẽ phát sự kiện này nhiều lần. |
| note.summary.generated | Bản tóm tắt đã tạo xong và có thể lấy được. |
| note.updated | Các trường hiển thị ra bên ngoài (tiêu đề, nội dung) thay đổi sau khi ghi chú kết thúc. Có debounce — các lần sửa liên tiếp trong thời gian ngắn được gộp thành một sự kiện. |
| note.deleted | Ghi chú đã bị xóa (data.reason: "deleted") hoặc đã ra khỏi phạm vi hiển thị của credential (data.reason: "access_lost"). |
Ngoài ra còn hai sự kiện dịch vụ: endpoint.verification (gửi khi tạo endpoint và khi đổi URL; phản hồi 2xx để kích hoạt endpoint) và endpoint.test (gửi khi bạn nhấn "Send test" trong console hoặc gọi API test).
Nội dung request
Webhook chỉ là thông báo gọn nhẹ: chúng mang theo định danh và trạng thái, không bao giờ chứa nội dung bản chép lời hay bản tóm tắt. Hãy dùng note_id để lấy nội dung qua REST API.
{
"event_id": "5f8c0a5e-...",
"event_type": "note.ended",
"occurred_at": "2026-08-12T05:20:00Z",
"data": {
"note_id": "note_abc123",
"recording_session_id": "rec_456",
"revision": 7,
"transcript_status": "ready",
"summary_status": "pending"
}
}Xác minh chữ ký
Mỗi lần gửi đều được ký theo chuẩn Standard Webhooks bằng secret whsec_... được cấp khi tạo endpoint:
POST /webhooks/alt HTTP/1.1
Content-Type: application/json
webhook-id: 5f8c0a5e-...
webhook-timestamp: 1765515600
webhook-signature: v1,K5oZfzN95Z9UVu1EsPQmSmZQGGVfCM0jaZ0lPI4dvLU=signed_content = "{webhook-id}.{webhook-timestamp}.{raw request body}"
signature = base64( HMAC-SHA256( base64url_decode(secret_after_whsec_), signed_content ) )- Xác minh trên raw body của request — không serialize lại JSON.
- So sánh chữ ký bằng hàm so sánh thời gian hằng định (constant-time).
- Từ chối các timestamp quá cũ (chúng tôi khuyến nghị sai số ±5 phút) để chống tấn công replay.
- Trả về 4xx cho request có chữ ký không khớp. Xem code receiver hoạt động được tại Bắt đầu nhanh, bước 4.
Gửi thất bại và retry
- Hãy phản hồi 2xx trong vòng 10 giây. Mọi trường hợp khác đều bị tính là thất bại. Trả lời trước, xử lý bất đồng bộ sau.
- Các lần gửi thất bại sẽ được retry với backoff tăng dần (30 giây → 5 phút → 30 phút → 2 giờ → 12 giờ → 12 giờ, tối đa 7 lần).
- Endpoint thất bại liên tiếp nhiều lần sẽ tự động bị vô hiệu hóa.
- Chúng tôi không đi theo redirect; URL webhook phải phản hồi trực tiếp qua HTTPS.
Xử lý sự kiện trùng lặp
Cơ chế gửi là at-least-once: cùng một sự kiện có thể đến nhiều hơn một lần (do retry, do gửi lại từ console). Hãy lưu lại event_id đã xử lý trong thời gian ngắn và bỏ qua các bản trùng.
Xử lý sự kiện đến sai thứ tự
Sự kiện có thể đến sai thứ tự (do retry, do gửi song song). Đừng cho rằng sự kiện đến sau cùng là trạng thái mới nhất. Mỗi thay đổi hiển thị ra bên ngoài đều làm tăng revision của ghi chú (do server gán, tăng đơn điệu): hãy lưu revision đã áp dụng cho từng ghi chú và bỏ qua mọi sự kiện có revision bằng hoặc thấp hơn. Khi không chắc chắn, hãy lấy lại ghi chú qua REST API — API luôn trả về revision hiện tại.
Kiểm tra thay đổi bị bỏ lỡ
- Webhook có thể bị bỏ lỡ (downtime dài hơn cửa sổ retry, endpoint bị vô hiệu hóa). Hãy định kỳ gọi
GET /v1/notes?updated_after=<last sync>để bắt kịp. note.deletedkèmreason: "access_lost"nghĩa là ghi chú đã ra khỏi phạm vi của credential (ví dụ: ghi chú cá nhân được chuyển vào một teamspace). Hãy xử lý y hệt như khi bị xóa: gỡ bỏ hoặc chặn truy cập vào bản sao bạn đang lưu.- Chỉ polling tăng dần thì không phát hiện được việc xóa hay mất phạm vi truy cập. Hãy định kỳ liệt kê toàn bộ ghi chú (dùng
include_deleted=trueđể thấy cả tombstone): mọi note ID bạn đang giữ mà không còn trong danh sách đều đã bị xóa hoặc đã ra khỏi phạm vi của bạn.