OdyOdy Trợ giúp

Nhận webhook và xác minh chữ ký

Cập nhật Sun Aug 30 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

Ody đẩy các sự kiện không gian làm việc — tin nhắn mới, kết quả cuộc gọi, thay đổi liên hệ — đến các điểm cuối HTTPS mà bạn đăng ký thông qua API, với mỗi lần gửi được ký theo thông số kỹ thuật Standard Webhooks để bạn có thể chứng minh rằng nó đến từ Ody.

Trước khi bắt đầu

  • Bạn cần một khóa API với phạm vi webhooks:manage — xem Lấy khóa Ody API của bạn.
  • Điểm cuối của bạn phải là một URL HTTPS phản hồi với trạng thái 2xx nhanh chóng (các lần gửi sẽ hết thời gian chờ sau 10 giây).
  • Mỗi không gian làm việc có thể đăng ký tối đa 50 điểm cuối.

Hướng dẫn từng bước

  1. Liệt kê các loại sự kiện bạn có thể đăng ký:
curl https://api.ody.co/v1/api/webhook-events \
  -H "Authorization: Bearer ody_live_…"

Danh mục hiện tại: message.received, message.delivered, message.failed, call.ringing, call.answered, call.completed, call.forwarded, call.missed, call.recording.completed, call.summary.completed, call.transcript.completed, call.voicemail.completed, call.flow.started, call.flow.completed, call.flow.aborted, contact.updated, contact.deleted. Đăng ký "*" bao gồm mọi loại sự kiện hiện tại và tương lai.

Các sự kiện call.flow.* kích hoạt khi một cuộc gọi được trả lời bởi một luồng cuộc gọi: started khi người gọi vào luồng, completed khi họ đến một đích (với loại đích — đổ chuông, trợ lý AI, thư thoại, chuyển tiếp hoặc gác máy), và aborted nếu luồng không thể hoàn thành.

  1. Đăng ký điểm cuối của bạn:
curl https://api.ody.co/v1/api/webhooks \
  -H "Authorization: Bearer ody_live_…" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/ody-webhook","events":["message.received"],"label":"prod"}'
  1. Lưu trữ secret từ phản hồi ngay lập tức. Khóa bí mật ký whsec_… chỉ được trả về khi tạo và xoay — nó sẽ không bao giờ được hiển thị lại.
  2. Gửi cho mình một sự kiện kiểm tra đã ký và xác nhận điểm cuối của bạn xác minh và chấp nhận nó:
curl -X POST https://api.ody.co/v1/api/webhooks/WEBHOOK_ID/test \
  -H "Authorization: Bearer ody_live_…"

Bài kiểm tra đăng một sự kiện {"type":"ping"} được ký chính xác như một lần gửi thực tế.

Một lần gửi trông như thế nào

Mỗi lần gửi là một POST với nội dung JSON và ba tiêu đề chữ ký:

{
  "id": "evt_…",
  "apiVersion": "2026-06-01",
  "createdAt": "2026-08-16T12:00:00.000Z",
  "type": "message.received",
  "data": { "conversationId": "…", "activityId": "…", "numberE164": "+1…" }
}
Tiêu đề Ý nghĩa
webhook-id ID sự kiện — loại bỏ trùng lặp dựa trên nó, vì các lần thử lại sử dụng lại nó
webhook-timestamp Giây Unix tại thời điểm gửi
webhook-signature v1,<base64 HMAC-SHA256> trên <id>.<timestamp>.<raw body>

Xác minh chữ ký

Khóa HMAC là phần đã giải mã base64 của khóa bí mật của bạn sau tiền tố whsec_, và thông điệp đã ký là <webhook-id>.<webhook-timestamp>.<raw body>. Luôn xác minh dựa trên byte yêu cầu thô — việc tuần tự hóa lại JSON đã phân tích cú pháp sẽ làm hỏng chữ ký. Từ chối các dấu thời gian quá vài phút để chặn các cuộc tấn công phát lại.

import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const app = express();
app.post("/ody-webhook", express.raw({ type: "application/json" }), (req, res) => {
  const secret = process.env.ODY_WEBHOOK_SECRET; // whsec_…
  const id = req.headers["webhook-id"];
  const timestamp = req.headers["webhook-timestamp"];
  const signature = req.headers["webhook-signature"];

  // Replay guard: reject deliveries older than 5 minutes.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(401).end();

  const key = Buffer.from(secret.slice("whsec_".length), "base64");
  const expected = "v1," + createHmac("sha256", key)
    .update(`${id}.${timestamp}.${req.body}`)
    .digest("base64");

  const a = Buffer.from(expected);
  const b = Buffer.from(String(signature));
  if (a.length !== b.length || !timingSafeEqual(a, b)) return res.status(401).end();

  const event = JSON.parse(req.body.toString("utf8"));
  console.log("verified event:", event.type);
  res.status(200).end();
});

Các SDK chính thức cung cấp chức năng này dưới dạng một dòng lệnh — verifyWebhookSignature (TypeScript) và verify_webhook_signature (Python), cả hai đều có so sánh thời gian không đổi và tính năng bảo vệ phát lại 5 phút được tích hợp sẵn. Xem Hướng dẫn nhanh SDK: TypeScript và Python.

Thử lại, lịch sử và quản lý

  • Thử lại: một lần gửi không thành công (không phải 2xx, hết thời gian chờ hoặc lỗi kết nối) sẽ được thử lại khoảng 5 giây và sau đó 30 giây sau lần thử đầu tiên — tổng cộng ba lần thử, mỗi lần hết thời gian chờ 10 giây.
  • Lịch sử: GET /v1/api/webhooks/:id/deliveries liệt kê các lần gửi gần đây nhất, với mã trạng thái, lỗi và thời lượng cho mỗi lần thử — điểm dừng đầu tiên của bạn khi các sự kiện dường như bị thiếu.
  • Quản lý điểm cuối: GET /v1/api/webhooks liệt kê chúng (không bao giờ hiển thị khóa bí mật), PATCH /v1/api/webhooks/:id cập nhật url, events, label hoặc status (enabled/disabled), và DELETE /v1/api/webhooks/:id xóa một điểm cuối cùng với lịch sử gửi của nó.
  • Xoay khóa bí mật bất cứ lúc nào bằng POST /v1/api/webhooks/:id/rotatewhsec_… mới được trả về một lần, và các chữ ký cũ sẽ ngừng xác thực ngay lập tức.

Khắc phục sự cố

  • Xác minh chữ ký luôn thất bại — có thể bạn đang xác minh một nội dung đã được tuần tự hóa lại. Hãy sử dụng các byte yêu cầu thô và xác nhận rằng bạn đã giải mã base64 khóa bí mật sau khi loại bỏ whsec_.
  • Không có lần gửi nào đến — hãy kiểm tra status của điểm cuối có phải là enabled không, danh sách events của nó có bao gồm loại bạn mong đợi (hoặc "*" ) không, và lịch sử gửi để tìm lỗi cho mỗi lần thử.
  • Sự kiện trùng lặp — các lần thử lại sử dụng lại cùng một webhook-id. Hãy loại bỏ trùng lặp dựa trên nó.
  • webhook limit reached — bạn đã đạt 50 điểm cuối cho không gian làm việc; hãy xóa một điểm cuối không sử dụng trước.

Các bài viết liên quan

Các câu hỏi thường gặp

Các lần gửi webhook được ký như thế nào?

Theo thông số kỹ thuật Standard Webhooks: một tiêu đề webhook-signature có dạng v1,<base64 HMAC-SHA256> được tính toán trên '<webhook-id>.<webhook-timestamp>.<raw body>' với khóa bí mật whsec_ của bạn.

Điều gì xảy ra nếu điểm cuối của tôi bị lỗi?

Ody thử lại hai lần — khoảng 5 giây và sau đó 30 giây sau lần thất bại đầu tiên. Kiểm tra GET /v1/api/webhooks/:id/deliveries để biết mã trạng thái và lỗi cho mỗi lần thử.

Tôi đã mất khóa bí mật ký của mình — bây giờ phải làm gì?

Xoay nó bằng POST /v1/api/webhooks/:id/rotate. Khóa bí mật whsec_ mới được trả về một lần; các chữ ký cũ sẽ ngừng xác thực ngay lập tức.

Xem thêm trong API dành cho nhà phát triển