API reference @cogover/sdk: Notification và email
Trang này thuộc API reference của @cogover/sdk.
Notification
notifications lưu một notification vào danh sách thông báo (biểu tượng chuông) của
từng người nhận và gửi nó trong web app, qua web push, mobile push và một bản sao
email, theo các kênh thông báo (notification channel) của workspace. Khác với
push message, notification vẫn nằm trong danh sách sau khi đã được
gửi, nên phù hợp với việc một người cần xử lý sau, ví dụ yêu cầu phê duyệt. Việc gửi
diễn ra bất đồng bộ và là best-effort: lời gọi hoàn tất khi Cogover đã tiếp nhận
notification, không phải khi nó tới thiết bị hay hộp thư.
type NotificationContentType = "text" | "html";
type NotificationDisplay = "SIMPLE" | "FULL";
type NotificationSender = "workspace" | "actor";
type NotificationLinkTarget = "SAME_TAB" | "NEW_TAB";
interface NotificationLink {
readonly url: string;
readonly openIn?: NotificationLinkTarget;
}
interface NotificationMessage {
readonly to: readonly string[];
readonly exclude?: readonly string[];
readonly title: string;
readonly content: string;
readonly contentType?: NotificationContentType;
readonly subtitle?: string;
readonly display?: NotificationDisplay;
readonly link?: NotificationLink;
readonly sender?: NotificationSender;
readonly channel?: string;
readonly email?: boolean;
readonly idempotencyKey?: string;
}
interface NotificationResult {
readonly requestId: string;
readonly notified: number;
readonly skippedPersonnelIds: readonly string[];
readonly duplicate: boolean;
}
interface NotificationsApi {
send(message: NotificationMessage): Promise<NotificationResult>;
}const result = await notifications.send({
to: approverIds,
exclude: ["actor"],
title: "Leave request waiting for your approval",
content: "A leave request of 3 days was submitted and needs your decision.",
link: { url: `/app/leave_request/${recordId}` },
channel: approvalChannelId,
idempotencyKey: `leave-approval:${recordId}`,
});
// result.notified: số tài khoản được gửi; result.skippedPersonnelIds: personnel chưa có tài khoảnnotifications.send(message): Promise<NotificationResult>
Người nhận. to liệt kê từ 1 đến 200 personnel ID; ID trùng được loại bỏ.
Notification được gửi tới tài khoản người dùng của từng personnel. Personnel chưa có
tài khoản người dùng được bỏ qua và trả về trong skippedPersonnelIds, còn
notified là số tài khoản được gửi sau khi đã loại trừ. exclude liệt kê personnel ID
không bao giờ nhận notification; giá trị đặc biệt "actor" là người dùng có hành động
khởi đầu execution hiện tại (người gọi HTTP route, người lưu record làm trigger chạy,
người có lời gọi đã enqueue lần chạy job). Execution không có người dùng thì không có
actor, nên "actor" không loại ai. Personnel ID trong to không tồn tại ném
NotFoundError với resource là "personnel" trước khi gửi bất cứ gì. Khi mọi người
nhận đều bị loại trừ hoặc bỏ qua, không có gì được gửi và notified là 0.
Nội dung và cách hiển thị.
-
title(bắt buộc, tối đa 200 ký tự) là tiêu đề của notification và là subject của bản sao email. -
content(bắt buộc, tối đa 16.384 ký tự) là nội dung. VớicontentType: "text"(mặc định), đây là văn bản thuần. Với"html", nội dung được rút gọn về định dạng cơ bản trước khi lưu — đoạn văn, tiêu đề, danh sách, bảng, liên kết và nhấn mạnh; script, style và hình ảnh bị loại bỏ — và vẫn phải còn chữ hiển thị được, nếu không sẽ némValidationError. -
subtitle(tối đa 200 ký tự) chỉ hiển thị ở chế độ"FULL".displaymặc định là"FULL"khi có subtitle và"SIMPLE"khi không có; subtitle đi kèmdisplay: "SIMPLE"némValidationError. -
linklà trang được mở khi người nhận bấm vào notification.urllà một path của web app Cogover bắt đầu bằng/, hoặc một URL tuyệt đốihttphayhttps, tối đa 2.048 ký tự.openInlà"SAME_TAB"hoặc"NEW_TAB"; mặc định"SAME_TAB"với path và"NEW_TAB"với URL tuyệt đối. -
senderlà"workspace"(mặc định) hoặc"actor", hiển thị tên và ảnh đại diện của người dùng có hành động khởi đầu execution."actor"trong execution không có người dùng némValidationError.
Kênh thông báo và bản sao email. channel là record ID của một notification
channel của workspace. Kênh quyết định hình thức gửi nào được bật — trong web app,
web push, mobile push và email — và mỗi người dùng có thể ghi đè lựa chọn đó trong
cài đặt thông báo của riêng mình. Không có channel thì mọi hình thức gửi đều được
bật. Kênh không tồn tại ném NotFoundError với resource là "notificationChannel";
workspace chưa thiết lập notification ném NotFoundError với resource là
"object" và resourceId là "notification_channel".
email: false bỏ bản sao email kể cả khi kênh bật email. Bản sao dùng title làm
subject và content làm nội dung, được gửi từ một địa chỉ thông báo của Cogover chứ
không phải từ hộp thư của workspace, và được tính vào hạn mức email thông báo hằng
ngày theo gói của workspace. Để gửi email từ hộp thư của workspace, dùng
email.send.
Idempotency. idempotencyKey bắt đầu bằng chữ cái hoặc chữ số và chứa tối đa 128
chữ cái, chữ số, ., _, : hoặc -. Lời gọi có key mà project đã dùng cho một
notification trong 7 ngày gần nhất sẽ không gửi gì và trả về kết quả của lời gọi đầu
tiên với duplicate: true. Lời gọi thực hiện trong lúc lời gọi đầu tiên cùng key vẫn
đang gửi ném CogoverApiError với code: "DUPLICATE_IN_PROGRESS"; hãy thử lại sau
giây lát. Lời gọi thất bại không giữ key, nên có thể thử lại. Key thuộc về project và
tách biệt với key của email.send. Trong trigger after-change hoặc job, vốn có thể
chạy nhiều hơn một lần, hãy tạo key từ trigger.changeId và record.id, hoặc từ
job.id hay một key nghiệp vụ. Cogover không so sánh nội dung: lời gọi dùng lại key với
người nhận hoặc nội dung khác vẫn trả về kết quả đầu tiên và không gửi gì.
Quy tắc
-
notificationscó trong script, route, trigger after-change, background job, lời gọi inbound webhook và Development Session có quyền ghi. Trigger before-change là chỉ đọc và nhậnPermissionDeniedErrorcódetails.reasonlà"TRIGGER_READ_ONLY"; preview chỉ đọc và Development Session chỉ đọc cũng từ chối. - Notification không cần grant trong identity policy: project được gửi notification tới mọi personnel của workspace.
-
Mặc định mỗi project được gửi tới tối đa 5.000 người nhận mỗi giờ. Lời gọi làm vượt
hạn mức này ném
RateLimitErrorvà không gửi gì. -
Input được SDK kiểm tra và Cogover kiểm tra lại; input không hợp lệ ném
ValidationErrortrước khi gửi bất cứ gì. Mỗi lời gọi tính một capability call. -
Khi deployment tắt notification, mọi lời gọi ném
CogoverApiErrorvới codeNOTIFICATIONS_DISABLED. -
Notification đã gửi không thể thu hồi. Chạy lại một execution thất bại có thể gửi
lại notification nếu lời gọi không dùng
idempotencyKey.
email gửi email từ hộp thư của workspace: một hộp thư dùng chung của workspace, hoặc
hộp thư cá nhân mặc định của người dùng có hành động khởi đầu execution. Identity
policy đã duyệt của Project quyết định project được dùng những hộp thư nào. Khi có
record, email còn được ghi nhận là một hoạt động email trên timeline của record đó,
giống email gửi từ trang record. Việc gửi diễn ra bất đồng bộ: lời gọi hoàn tất khi
Cogover đã tiếp nhận email để gửi, và lỗi gửi xảy ra sau đó không được báo lại cho
script.
type EmailSender = { readonly mailbox: string } | "actor";
type EmailRecipient =
| string
| { readonly email: string; readonly name?: string }
| { readonly personnelId: string };
interface EmailRecordLink<ObjectSlug extends string = string> {
readonly object: ObjectSlug;
readonly recordId: string;
}
interface EmailAttachment<ObjectSlug extends string = string> {
readonly object: ObjectSlug;
readonly recordId: string;
readonly field: string;
}
type EmailMessage<ObjectSlug extends string = string> = {
readonly from: EmailSender;
readonly to?: readonly EmailRecipient[];
readonly cc?: readonly EmailRecipient[];
readonly bcc?: readonly EmailRecipient[];
readonly subject: string;
readonly record?: EmailRecordLink<ObjectSlug>;
readonly recordEmailFields?: readonly string[];
readonly attachments?: readonly EmailAttachment<ObjectSlug>[];
readonly appendSignature?: boolean;
readonly idempotencyKey?: string;
} & (
| { readonly html: string; readonly text?: never }
| { readonly text: string; readonly html?: never }
);
type EmailDelivery = "activity" | "direct";
interface EmailSendResult {
readonly requestId: string;
readonly delivery: EmailDelivery;
readonly recipients: number;
readonly duplicate: boolean;
}
type EmailSenderKind = "workspace" | "actor";
interface EmailSenderInfo {
readonly id: string;
readonly kind: EmailSenderKind;
readonly email: string;
readonly displayName: string;
}
interface EmailApi<Schema extends object = EffectiveWorkspaceObjects> {
send(message: EmailMessage<StringKeyOf<Schema>>): Promise<EmailSendResult>;
senders(): Promise<readonly EmailSenderInfo[]>;
}const result = await email.send({
from: { mailbox: "EMWXXXXXXXXXXXX" },
bcc: ["sales-archive@example.com"],
subject: `Quote ${quote.fields.code}`,
html: "<p>Dear customer,</p><p>Please find our quote attached.</p>",
record: { object: "quote", recordId: quote.id },
recordEmailFields: ["contact_email"],
attachments: [{ object: "quote", recordId: quote.id, field: "quote_pdf" }],
idempotencyKey: `quote-email:${quote.id}`,
});
// result.delivery === "activity": email xuất hiện trên timeline của báo giá.email.send(message): Promise<EmailSendResult>
Người gửi. from: { mailbox: id } gửi từ một hộp thư dùng chung của workspace;
from: "actor" gửi từ hộp thư cá nhân mặc định của người dùng có hành động khởi đầu
execution. Người gửi phải được phép trong mục email của identity policy đã duyệt
của Project: workspaceMailboxIds liệt kê các hộp thư dùng chung và
allowActorMailbox cho phép "actor". Giống phần còn lại của policy, mục này chỉ áp
dụng cho các caller được chọn trong callerPersonnelIds, và cho execution không có
người dùng chỉ khi policy đặt allowInternalSystem: true. Project có policy không có
mục email thì không gửi được email. Người gửi không được phép ném
PermissionDeniedError có details.reason là "EMAIL_SENDER_NOT_GRANTED"; bước kiểm
tra này diễn ra trước mọi bước tra cứu khác. Hộp thư không tồn tại hoặc đã bị vô hiệu
hoá, và actor không có hộp thư cá nhân đang hoạt động, ném NotFoundError với
resource là "mailbox". "actor" trong execution không có người dùng ném
ValidationError.
Người nhận.
-
to,ccvàbccnhận một chuỗi địa chỉ,{ email, name? }hoặc{ personnelId }. Personnel được đổi thành email công việc của họ, hoặc email của tài khoản người dùng khi không có email công việc; personnel không có cả hai némValidationError, và personnel không tồn tại némNotFoundErrorvớiresourcelà"personnel". -
Địa chỉ trùng được loại bỏ trong từng danh sách, không phân biệt hoa thường. Ba danh
sách cộng lại nhận tối đa 50 người nhận.
namelà một dòng, tối đa 200 ký tự. tophải có ít nhất một người nhận, trừ khi córecordEmailFields.
Nội dung. subject là bắt buộc: một dòng, tối đa 255 ký tự. Cung cấp đúng một
trong html và text; nội dung tối đa 192 KB (196.608 byte) theo UTF-8, đo trên dạng
đã mã hoá JSON: mỗi dấu ngoặc kép, dấu gạch chéo ngược hoặc ký tự điều khiển như xuống
dòng được tính theo chuỗi escape của nó (\" là 2 byte, \n là 2 byte, \u0001 là 6
byte). Nội dung dài hơn ném ValidationError trước khi gọi.
Record. record: { object, recordId } ghi nhận email là một hoạt động email trên
timeline của record đó: Cogover theo dõi lượt mở và lượt bấm liên kết, gom các thư
trả lời vào cùng luồng, và result.delivery là "activity". Record phải đọc được
bằng danh tính mặc định của execution: record không tồn tại ném NotFoundError với
resource là "record", còn record mà danh tính đó không có quyền đọc ném
PermissionDeniedError. Không có record thì email chỉ được gửi từ hộp thư và
delivery là "direct". Các tuỳ chọn sau cần record; thiếu record chúng ném
ValidationError:
-
recordEmailFieldsliệt kê tối đa 20 field email của record đó, địa chỉ trong các field này được thêm vàoto. Field không phải field email némValidationError. -
attachmentsđính kèm mọi file lưu trong file fieldfieldcủa từng record được liệt kê: tối đa 10 file và tổng cộng 20 MB. Các record phải đọc được bằng danh tính mặc định; field không phải file field némValidationError. -
appendSignature: truethêm chữ ký email mặc định của người gửi. Tuỳ chọn này cần hộp thư cá nhân, tứcfrom: "actor"; nếu không sẽ némValidationError.
Kết quả. requestId định danh lần gửi. recipients đếm các địa chỉ trong to,
cc và bcc sau khi loại trùng; địa chỉ của recordEmailFields được xác định sau
nên không được đếm. duplicate là true khi một lời gọi trước đó cùng
idempotencyKey đã gửi email.
Idempotency. idempotencyKey theo cùng quy tắc với
notification: lời gọi lặp lại cùng key trong 7 ngày trả về kết quả
đầu tiên với duplicate: true thay vì gửi lại, và lời gọi lặp lại trong lúc lời gọi
đầu tiên vẫn đang gửi ném CogoverApiError với code: "DUPLICATE_IN_PROGRESS". Key
của email tách biệt với key của notification. Giống notification, nội dung không được
so sánh: dùng lại key cho một email khác vẫn trả về kết quả đầu tiên và không gửi gì.
email.senders(): Promise<readonly EmailSenderInfo[]>
Trả về các hộp thư đang hoạt động mà execution này được dùng để gửi: một mục
kind: "workspace" cho mỗi hộp thư dùng chung đang hoạt động được policy áp dụng liệt
kê trong workspaceMailboxIds, dùng dưới dạng { mailbox: id }, và, khi policy đặt
allowActorMailbox và actor có hộp thư cá nhân đang hoạt động, tối đa một mục
kind: "actor" mô tả hộp thư mà from: "actor" sử dụng. Danh sách rỗng khi policy
không cấp người gửi nào cho execution này. Dùng method này để hiển thị các người gửi
khả dụng hoặc chọn người gửi theo địa chỉ email thay vì hard-code ID. Method không
gửi gì, nên cũng dùng được trong preview chỉ đọc và Development Session chỉ đọc.
Quy tắc
-
emailcó trong script, route, trigger after-change, background job, lời gọi inbound webhook và Development Session. Trigger before-change nhậnPermissionDeniedErrorcódetails.reasonlà"TRIGGER_READ_ONLY"với cả hai method; preview chỉ đọc và Development Session chỉ đọc từ chốiemail.send. -
Mặc định mỗi project được gửi tối đa 500 email mỗi giờ; vượt quá sẽ ném
RateLimitErrorvà không gửi gì. Một lời gọi là một email, bất kể có bao nhiêu người nhận. -
Input được SDK kiểm tra và Cogover kiểm tra lại; input không hợp lệ ném
ValidationErrortrước khi gửi bất cứ gì. Mỗi lời gọi tính một capability call. -
Khi deployment tắt email, mọi lời gọi ném
CogoverApiErrorvới codeEMAIL_DISABLED. -
Email đã gửi không thể thu hồi. Chạy lại một execution thất bại có thể gửi lại email
nếu lời gọi không dùng
idempotencyKey.
Rate this page
On this page