Tài liệu dành cho nhà phát triển
Bắt đầu nhanh
Tạo API key, gửi request đầu tiên và nhận webhook an toàn chỉ trong bốn bước.
1. Tạo API key
Tạo một tích hợp trong console tài khoản và cấp API key. Key đầy đủ chỉ hiển thị một lần, vì vậy hãy lưu an toàn trong secret manager.
- Tài khoản → API & Webhooks — tạo một tích hợp cho workspace cá nhân hoặc teamspace mà bạn sở hữu.
- Chọn phạm vi quyền cần thiết:
notes:read,transcripts:read,summaries:read,webhooks:manage. - Key có dạng
alt_live_{key_id}.{secret}và chỉ hiển thị một lần. Hãy lưu vào secret manager.
Export key trong shell để mọi lệnh bên dưới chạy được ngay:
export ALT_API_KEY="alt_live_...paste-your-key-here..."2. Lấy các ghi chú hiện có
Gửi cursor do API trả về trong request tiếp theo để đi hết danh sách. Sau đó lấy bản chép lời và bản tóm tắt của từng ghi chú.
curl 'https://public-api.altalt.io/v1/notes?limit=100' \
-H "Authorization: Bearer $ALT_API_KEY"
# Follow next_cursor until has_more is false
curl 'https://public-api.altalt.io/v1/notes?limit=100&cursor=NEXT_CURSOR' \
-H "Authorization: Bearer $ALT_API_KEY"
# Fetch content per note (scopes: transcripts:read / summaries:read)
curl 'https://public-api.altalt.io/v1/notes/NOTE_ID/transcript' \
-H "Authorization: Bearer $ALT_API_KEY"
curl 'https://public-api.altalt.io/v1/notes/NOTE_ID/summary' \
-H "Authorization: Bearer $ALT_API_KEY"Sau đó, để đồng bộ tăng dần, hãy polling với ?updated_after=<last sync time> hoặc dùng webhook.
3. Đăng ký webhook endpoint
Đăng ký một endpoint HTTPS công khai để được thông báo về ghi chú mới và ghi chú thay đổi, thay vì phải polling.
curl -X POST 'https://public-api.altalt.io/v1/webhook-endpoints' \
-H "Authorization: Bearer $ALT_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/webhooks/alt",
"events": ["note.ended", "note.summary.generated", "note.updated", "note.deleted"]
}'Phản hồi có chứa signing_secret (whsec_...) — chỉ hiển thị một lần. Endpoint khởi đầu ở trạng thái pending_verification và chuyển sang hoạt động sau khi receiver của bạn phản hồi 2xx cho sự kiện xác minh. Bạn cũng có thể làm việc này mà không cần viết code trong console.
4. Xác minh chữ ký webhook
Xác minh chữ ký Standard Webhooks của mọi webhook request để chắc chắn request đến từ Alt. Bỏ qua sự kiện trùng bằng event_id, phản hồi trước rồi lấy nội dung mới nhất qua REST API.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
import http from "node:http";
// whsec_... secret from endpoint creation (shown once). Keep it server-side.
const SECRET = process.env.ALT_WEBHOOK_SECRET;
const secretBytes = Buffer.from(SECRET.slice("whsec_".length), "base64url");
const TOLERANCE_SECONDS = 300;
function isValidSignature(headers, rawBody) {
const id = headers["webhook-id"];
const timestamp = headers["webhook-timestamp"];
const signatureHeader = headers["webhook-signature"];
if (!id || !timestamp || !signatureHeader) return false;
// Reject stale timestamps (replay protection)
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false;
const expected = createHmac("sha256", secretBytes)
.update(`${id}.${timestamp}.${rawBody}`)
.digest("base64");
// Header may contain multiple space-delimited signatures: "v1,abc v1,def"
return String(signatureHeader)
.split(" ")
.some((part) => {
const [version, signature] = part.split(",");
if (version !== "v1" || !signature) return false;
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
});
}
http
.createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/webhooks/alt") {
res.writeHead(404).end();
return;
}
let rawBody = "";
req.on("data", (chunk) => (rawBody += chunk));
req.on("end", () => {
if (!isValidSignature(req.headers, rawBody)) {
res.writeHead(401).end();
return;
}
const event = JSON.parse(rawBody);
// 1. Dedupe on event.event_id (deliveries are at-least-once).
// 2. Enqueue for async processing, then ack fast.
// 3. Fetch the note from the REST API; apply only if revision is newer.
console.log(event.event_type, event.data.note_id, event.data.revision);
res.writeHead(204).end();
});
})
.listen(3000);Python
import base64, hashlib, hmac, json, os, time
from http.server import BaseHTTPRequestHandler, HTTPServer
# whsec_... secret from endpoint creation (shown once). Keep it server-side.
raw_secret = os.environ["ALT_WEBHOOK_SECRET"].removeprefix("whsec_")
SECRET = base64.urlsafe_b64decode(raw_secret + "=" * (-len(raw_secret) % 4))
TOLERANCE_SECONDS = 300
def is_valid_signature(headers, raw_body: bytes) -> bool:
msg_id = headers.get("webhook-id", "")
timestamp = headers.get("webhook-timestamp", "")
signature_header = headers.get("webhook-signature", "")
if not msg_id or not timestamp or not signature_header:
return False
# Reject stale timestamps (replay protection)
if abs(time.time() - float(timestamp)) > TOLERANCE_SECONDS:
return False
signed_content = f"{msg_id}.{timestamp}.".encode() + raw_body
digest = hmac.new(SECRET, signed_content, hashlib.sha256).digest()
expected = base64.b64encode(digest).decode()
# Header may contain multiple space-delimited signatures: "v1,abc v1,def"
for part in signature_header.split(" "):
version, _, signature = part.partition(",")
if version == "v1" and signature and hmac.compare_digest(signature, expected):
return True
return False
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path != "/webhooks/alt":
self.send_response(404); self.end_headers(); return
raw_body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
if not is_valid_signature(self.headers, raw_body):
self.send_response(401); self.end_headers(); return
event = json.loads(raw_body)
# 1. Dedupe on event["event_id"] (deliveries are at-least-once).
# 2. Enqueue for async processing, then ack fast.
# 3. Fetch the note from the REST API; apply only if revision is newer.
print(event["event_type"], event["data"]["note_id"], event["data"]["revision"])
self.send_response(204); self.end_headers()
HTTPServer(("", 3000), Handler).serve_forever()Chữ ký tuân theo chuẩn Standard Webhooks, nên các thư viện standardwebhooks chính thức (npm / PyPI) cũng dùng được. Xem Webhooks để biết cách xử lý trùng lặp, sai thứ tự và đối soát.