Cogover Learning

API reference @cogover/sdk: Inbound webhook

Trang này thuộc API reference của @cogover/sdk.

Inbound webhook

Quản trị viên có thể tạo inbound access cho project để một hệ thống bên ngoài gọi route mà không cần phiên người dùng Cogover. Hệ thống bên ngoài nhận URL có dạng

https://{WORKSPACE_DOMAIN}/api/v1/ts-projects/{projectSlug}/hooks/{inboundId}/{route}

và xác thực mỗi lời gọi bằng inbound key hoặc chữ ký HMAC đã cấu hình cho inbound access. Cogover kiểm tra key hoặc chữ ký, áp dụng rate limit và từ chối lời gọi trước khi bất kỳ code nào của project chạy. Route handler thấy lời gọi như vậy với request.path === "/hooks/{route}": đăng ký route webhook dưới /hooks/, và inbound ID không nằm trong path.

import { createRouter } from "@cogover/sdk";
 
const router = createRouter();
router.post("/hooks/payments", async ({ request, invocation, crypto, jobs, response }) => {
  if (invocation.identity !== "inbound") return response.empty({ status: 403 });
 
  // Tuỳ chọn: kiểm tra chữ ký riêng của hệ thống bên ngoài trên raw body.
  const expected = await crypto.hmacSha256({ secret: "payment_signing_secret" }, request.rawBody ?? "");
  if (!crypto.timingSafeEqual(expected, request.headers["x-payment-signature"] ?? "")) {
    return response.empty({ status: 401 });
  }
 
  const event = request.body as { id: string; orderId: string };
  await jobs.enqueue("sync_payment", event, { idempotencyKey: `payment:${event.id}` });
  return response.json({ received: true }, { status: 202 });
});
export default router.toHandler();
  • Danh tính. invocation.identity là "inbound", invocation.user là null và invocation.inbound chứa id, name và mode xác thực của inbound access. data.object() thực hiện dưới danh tính system và chỉ hoạt động nếu identity policy đã duyệt của project đặt allowInternalSystem: true; khi đó nó có cùng quyền truy cập như trong job chạy theo lịch, không bị thu hẹp bởi các object được cấp cho data.asSystem(). Nếu không, các lời gọi của nó ném PermissionDeniedError có details.reason là "IDENTITY_NOT_GRANTED". data.asUser() và data.asSystem() chỉ tuân theo quyền được cấp trong identity policy khi allowInternalSystem là true; nếu không, chúng cũng bị từ chối như vậy, kể cả với object đã được cấp.
  • Body. Request body giới hạn 256 KiB. Khi body là JSON object, nó là request.body; body dạng form, text hoặc dạng khác để request.body rỗng ({}). request.rawBody luôn chứa body đúng như đã nhận, dưới dạng chuỗi UTF-8, và request.contentType là Content-Type của request, nên handler có thể kiểm tra chữ ký hoặc tự parse định dạng khác.
  • Response. Giá trị trả về của handler và các helper response hoạt động như với mọi route. Giữ handler ngắn: xác nhận sự kiện rồi enqueue một job cho việc thực sự, để hệ thống bên ngoài nhận trả lời nhanh và các lần retry không chồng lên xử lý chậm.
  • Retry. Hệ thống bên ngoài thường gửi lại webhook không nhận được response 2xx, nên handler có thể thấy cùng một sự kiện nhiều lần. Dùng định danh riêng của sự kiện làm idempotencyKey cho job được enqueue, hoặc làm key trong state. Header Idempotency-Key hoạt động như với mọi route. Khi inbound access kiểm tra chữ ký HMAC có timestamp, mỗi chữ ký chỉ được chấp nhận một lần, trước khi handler chạy: lần retry lặp lại cùng chữ ký bị từ chối với HTTP 401, kể cả khi lần đầu thất bại, nên bên gửi phải ký lại cho mỗi lần retry.

© 2026 Cogover LLC