Agent hiếm khi chỉ do người dùng bấm nút. Nó thường được kích hoạt bởi sự kiện: tin nhắn mới trên Slack, email đến, thanh toán thành công, comment GitHub. Webhook là cách hệ thống ngoài "gọi" agent của bạn. Bài này chỉ cách nhận webhook đúng và trả kết quả bất đồng bộ.
Webhook đi hai chiều
- Inbound (bạn nhận): dịch vụ ngoài POST vào endpoint của bạn để kích hoạt agent.
- Outbound (bạn gửi): agent chạy xong, bạn POST kết quả về endpoint của client/hệ thống khác.
Quy tắc vàng: trả 200 NGAY, xử lý sau
Hầu hết nhà cung cấp webhook (Slack, Stripe, GitHub) timeout 3–10 giây và sẽ gửi lại nếu bạn không trả 2xx kịp. Agent chạy 30 giây → chắc chắn timeout → họ gửi lại → agent chạy nhiều lần. Đúng cách:
@app.post("/webhook/slack")
def slack_webhook(req):
if not verify_signature(req): # 1. Xác thực trước
return Response(status=401)
event = req.json
if seen(event["event_id"]): # 2. Chống trùng
return Response(status=200)
queue.enqueue(event) # 3. Đẩy vào queue, KHÔNG chạy agent ở đây
return Response(status=200) # 4. Trả 200 ngay (<1s)
Nhận nhanh → đẩy vào queue (Bài 4) → worker chạy agent → trả kết quả qua outbound webhook.
Ba việc bắt buộc khi nhận webhook
1. Xác thực chữ ký (signature verification)
Bất kỳ ai cũng có thể POST vào URL của bạn. Kẻ xấu có thể giả sự kiện để kích hoạt agent tốn tiền. Luôn kiểm tra HMAC signature bằng secret dùng chung.import hmac, hashlib
def verify_signature(req):
sig = req.headers.get("X-Signature", "")
mac = hmac.new(SECRET, req.raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, sig) # so sánh chống timing attack
2. Idempotency theo event id
Webhook thường "at-least-once" → cùng một sự kiện có thể đến nhiều lần. Lưuevent_id đã xử lý và bỏ qua bản trùng.3. Trả nhanh, tách xử lý
Không bao giờ chạy agent ngay trong handler webhook.Gửi outbound webhook đáng tin
Khi agent xong, bạn POST kết quả về client. Endpoint của họ có thể tạm chết → bạn cần:
- Retry với backoff (như queue).
- Ký chữ ký để client tin đây là bạn.
- Timeout ngắn cho lời gọi outbound.
- Log delivery để debug khi client bảo "không nhận được".
Khung tư duy: 4 lớp phòng thủ webhook inbound
1. Verify -> đúng nguồn không?
2. Dedupe -> đã xử lý event này chưa?
3. Enqueue -> đẩy vào queue, không chạy nặng
4. Ack 200 -> trả nhanh để không bị gửi lại
Các bước triển khai
- Tạo endpoint riêng cho mỗi nguồn webhook.
- Cài verify signature bằng
hmac.compare_digest(không dùng==). - Lưu bảng
processed_events(event_id)để dedupe. - Đẩy event vào queue, trả 200 ngay.
- Với outbound: thêm retry backoff, ký payload, log mỗi lần gửi.
- Test replay: gửi lại đúng event, xác nhận agent không chạy lần hai.
Sai lầm thường gặp
- Chạy agent ngay trong handler webhook: bị timeout → nhà cung cấp gửi lại → agent chạy lặp, đốt tiền.
- Không verify signature: URL public bị lạm dụng để kích hoạt agent giả.
- Dùng
==so sánh chữ ký: dính timing attack; phải dùngcompare_digest. - Không dedupe theo event id: một sự kiện thật bị xử lý nhiều lần.
- Đọc body sau khi framework đã parse: hỏng việc tính HMAC trên raw body.
- Outbound không retry/log: client mất kết quả mà bạn không biết vì sao.
Học xong bài này rồi?
Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu,
và học hết khóa thì có chứng chỉ.
Lưu tiến độ của tôi