Cogover Learning

API reference: @cogover/sdk

@cogover/sdk là contract TypeScript công khai để phát triển Custom Backend Module trên Cogover. Thông tin xác thực của Cogover không bao giờ được đưa vào code của module; các capability call bị giới hạn bởi quyền và hạn mức đã cấu hình cho project.

Mục lục

Tài liệu tham chiếu được chia thành các trang sau. Mỗi trang mô tả một nhóm chức năng của SDK.

Trang Nội dung
Script và HTTP route defineScript; HTTP router và request context; Danh tính invocation và workspace hiện tại; HTTP response API; Logging
Record và schema Record API; Chọn field; Record liên kết; Aggregate; Filter và sort; Danh tính thực hiện thao tác record; Schema API; Khai báo Workspace
Record trigger Record trigger
Background job Background job
Outbound HTTP và secret Outbound HTTP fetch; Secret và credential
Mã hoá và chữ ký Mã hoá và chữ ký
Inbound webhook Inbound webhook
Project state và lock Project state; Distributed locks
Push message Push message
Notification và email Notification; Email
Cơ cấu tổ chức Cơ cấu tổ chức
Custom Module Action Custom Module Action; Schema builder
Process và AI Agent Process và AI Agent

Các mục dưới đây áp dụng cho mọi nhóm chức năng: các lỗi mà mọi API có thể ném, giới hạn của một lần thực thi, cách đọc các giới hạn đó trong code và helper tương thích.

Errors

class CogoverApiError extends Error {
  readonly code: string;
  readonly r: number | undefined;
  readonly details?: unknown;
  constructor(code: string, message: string, details?: unknown);
}
 
class NotFoundError extends CogoverApiError {
  readonly resource: string;
  readonly resourceId: string;
  constructor(resource: string, resourceId: string, message?: string, details?: unknown);
}
 
class ValidationError extends CogoverApiError {
  constructor(message: string, details?: unknown);
}
 
class PermissionDeniedError extends CogoverApiError {
  constructor(message: string, details?: unknown);
}
 
class RateLimitError extends CogoverApiError {
  constructor(message: string, details?: unknown);
}
 
class RetryableError extends CogoverApiError {
  constructor(message: string, details?: unknown);
}
 
class StateConflictError extends CogoverApiError {
  constructor(message: string, details?: unknown);
}
 
class LockUnavailableError extends CogoverApiError {
  constructor(key: string, message?: string, details?: unknown);
}
 
class LockLostError extends CogoverApiError {
  constructor(message: string, details?: unknown);
}

Các lỗi dịch vụ được SDK map sang các error trên. Các error giữ:

  • error.code: nhóm lỗi SDK, ví dụ PERMISSION_DENIED.
  • error.r: mã r gốc từ server, ví dụ 37; là undefined nếu không có response từ server (như lỗi validation trong SDK).
  • error.message: thông báo gốc; error.details giữ chi tiết kèm theo, gồm r.

RetryableError (code: "RETRYABLE") báo một lỗi tạm thời. SDK ném lỗi này cho thao tác ghi record mà trigger before-change không kiểm tra được (details.reason là "TRIGGER_FAILED", xem Record trigger) và cho lần khởi chạy Process hoặc AI Agent thất bại tạm thời (xem Process và AI Agent), và code của project tự ném lỗi này. Handler của trigger after-change hoặc job handler ném lỗi này để báo một lỗi tạm thời, và Cogover sẽ chạy lại handler sau (xem Quy tắc thực thi after-change và Quy tắc thực thi job). Ở những nơi khác lỗi này không có ý nghĩa đặc biệt: script để lỗi thoát ra sẽ trả HTTP 422, và trigger before-change ném lỗi này sẽ làm thao tác ghi bị từ chối như mọi lỗi khác.

Lệnh đọc record (get, getMany hoặc list) không có option fields hợp lệ ném ValidationError với message fields is required: pass field slugs or "*" (@cogover/sdk 0.13.0+). Cogover trả cùng lỗi này cho phiên bản project đã publish bằng SDK cũ mà đọc không có fields (xem Chọn field).

jobs.enqueue, secrets.get và crypto thất bại với ValidationError, PermissionDeniedError hoặc RateLimitError như mô tả trong mục của chúng, và với CogoverApiError có code là JOBS_DISABLED hoặc SECRETS_DISABLED khi tính năng chưa được bật cho Workspace. crypto.aesDecrypt và crypto.rsaDecrypt ném CogoverApiError với code: "DECRYPTION_FAILED" khi không giải mã được ciphertext.

notifications.send và email.send thất bại với ValidationError, NotFoundError, PermissionDeniedError hoặc RateLimitError như mô tả trong mục của chúng, với CogoverApiError có code là NOTIFICATIONS_DISABLED hoặc EMAIL_DISABLED khi tính năng bị tắt, và với CogoverApiError có code là DUPLICATE_IN_PROGRESS trong lúc một lời gọi trước đó cùng idempotencyKey vẫn đang gửi. Hộp thư gửi không được Project policy cho phép được báo bằng PermissionDeniedError có details.reason là "EMAIL_SENDER_NOT_GRANTED".

processes và agents thất bại với ValidationError, NotFoundError, PermissionDeniedError, RateLimitError hoặc RetryableError như mô tả ở Process và AI Agent. Process hoặc agent không được Project policy cho phép được báo bằng PermissionDeniedError có details.reason là "PROCESSES_NOT_ALLOWED" hoặc "AGENTS_NOT_ALLOWED", và approvalPolicy: "autoApprove" không được cho phép được báo với "AGENT_AUTO_APPROVE_NOT_ALLOWED". Hai API này còn thất bại với CogoverApiError có code là MAX_HOP_EXCEEDED khi ngân sách tự động hoá đã hết, IDEMPOTENCY_CONFLICT khi một danh tính khác của project đã dùng idempotencyKey, AGENT_DEPTH_EXCEEDED khi các AI Agent khởi chạy lẫn nhau quá sâu, NOT_SUPPORTED với onComplete hoặc onResult trong phiên phát triển local, và PROCESSES_DISABLED hoặc AGENTS_DISABLED khi tính năng không khả dụng. agents.start còn thất bại với CogoverApiError có code là INSUFFICIENT_CREDITS, AI_SUBSCRIPTION_REQUIRED, AI_SEAT_REQUIRED, AGENT_CREDIT_LIMIT_EXCEEDED hoặc MODEL_NOT_PRICED khi dịch vụ AI Agent từ chối lượt chạy vì tính phí credit AI. Node Process hoặc AI Agent gọi một Custom Module Action nhận các lỗi mô tả ở Lỗi bên gọi nhận được.

Script có thể bắt lỗi và tự chọn mã/thông báo trả cho client, không cần forward lỗi gốc. Thông báo public trong msg, message và các field tương tự luôn phải viết bằng tiếng Anh, kể cả khi tài liệu hoặc giao diện sử dụng ngôn ngữ khác.

try {
  await orders.records.update(id, fields);
} catch (error) {
  if (error instanceof CogoverApiError && error.r === 37) {
    return { r: 1001, msg: "You do not have permission to update this order." };
  }
  throw error;
}

Import CogoverApiError từ @cogover/sdk. JSON do script return vẫn là response do script tự định nghĩa với HTTP 200.

Với lỗi SDK thoát khỏi defineScript, endpoint HTTP trả lỗi có r bằng HTTP status, code và msg an toàn:

SDK code HTTP / r
PERMISSION_DENIED 403
NOT_FOUND 404
METHOD_NOT_ALLOWED 405
VALIDATION_ERROR 400
RATE_LIMITED 429
STATE_CONFLICT 409
LOCK_NOT_ACQUIRED 409
LOCK_LOST 409
STATE_SERVICE_UNAVAILABLE 503
LOCK_SERVICE_UNAVAILABLE 503
ORGANIZATION_UNAVAILABLE 503
FETCH_DISABLED 503
FETCH_BLOCKED 400
FETCH_REQUEST_TOO_LARGE 413
FETCH_RESPONSE_TOO_LARGE 502
FETCH_TIMEOUT 504
FETCH_FAILED 502
COGOVER_API_ERROR 422
RETRYABLE 422

Thiếu phê duyệt danh tính được chọn trả thêm reason: "IDENTITY_NOT_GRANTED"; không có nghĩa nhân sự đích đã bị từ chối truy cập một bản ghi cụ thể. Không trả thông báo lỗi gốc, stack trace hoặc details tuỳ ý. writesMayHaveCompleted là true nếu lần thực thi đã gửi ít nhất một lời gọi có thể thay đổi dữ liệu bên ngoài, kể cả khi thao tác sau đó thất bại: ghi record, state.set hoặc state.delete, mọi lời gọi locks (kể cả acquire trả null), push message, notifications.send, email.send, jobs.enqueue, processes.start, agents.start, hoặc fetch có method khác GET và HEAD. Vì vậy LockUnavailableError thoát khỏi script luôn báo true. Đây là cảnh báo thận trọng, không xác nhận ghi thành công. Lỗi không xác định và lỗi giới hạn tài nguyên vẫn trả HTTP 422 chung. Các phản hồi này không rollback thao tác ghi trước đó; không tự động retry ghi.

Giới hạn của một lần thực thi

Mỗi lần thực thi chạy trong giới hạn của nền tảng. Các giá trị dưới đây là mặc định; chúng do nền tảng thiết lập và có thể khác giữa các môi trường.

Ngân sách của một lần thực thi HTTP route Trigger before-change Trigger after-change Background job
Capability call (capabilityCalls) 100 20 100 1.000
Lời gọi cục bộ (localCalls) 200 50 200 1.000
Record đọc (recordsRead) 10.000 4.000 10.000 50.000
Record ghi (recordsWritten) 10.000 — 10.000 50.000
  • Capability call. Mỗi lời gọi data, schema, state, locks, push, notifications, email, jobs, secrets, fetch, processes hoặc agents, và mỗi lần đọc org có withDisplay: true, tính một lần.
  • Lời gọi cục bộ. Lời gọi crypto và lần đọc org không có withDisplay do chính Cogover xử lý, nên tính vào ngân sách riêng. Key crypto dạng { secret: name } vẫn là một lần đọc secret (xem quota bên dưới).
  • Record đọc. Các record mà records.get, records.getMany và records.list trả về, cùng các record liên kết mà expandLookups đọc. Một lần đọc bị từ chối trước khi gửi nếu có thể vượt ngân sách: records.get tính 1, records.getMany tính số ID, còn records.list tính limit của nó (mặc định 20). records.aggregate không trả record nên không tính.
  • Record ghi. create, update và upsertByUniqueField tính 1, batchInsert và batchUpdate tính số record, deleteMany tính số ID, được tính khi lời gọi được gửi. Trigger before-change không ghi được record.
  • Lời gọi có thể vượt một ngân sách, và mọi lời gọi sau đó thuộc ngân sách đó, ném RateLimitError mà không được gửi đi, nên script có thể bắt được. details.budget là tên ngân sách, details.limit là kích thước, details.used là phần lần thực thi đã dùng, và với record thì details.requested là số lời gọi yêu cầu. Chờ không giúp được gì: ngân sách thuộc về lần thực thi. Hãy đọc ít record hơn, dùng records.aggregate cho số tổng, hoặc chuyển việc sang background job. Lần thực thi vẫn tiếp tục gọi sau 1.000 lời gọi bị từ chối sẽ bị kết thúc với code: "RATE_LIMITED"; script không bắt được lỗi này.
  • Một HTTP route có 8 giây wall-clock, tính từ lúc handler bắt đầu chạy, kể cả thời gian chờ capability call, chờ lock hoặc chờ server bên ngoài. Record trigger dùng timeoutMs của nó, background job dùng timeoutMs của job. Custom Module Action có ngân sách của một HTTP route và timeoutMs riêng, tối đa 8 giây. Vượt thời gian sẽ kết thúc toàn bộ lần thực thi: script không bắt được, và HTTP route trả response HTTP 422 chung. Các capability call của một lần thực thi chạy lần lượt, kể cả khi được bắt đầu cùng lúc bằng Promise.all.
  • Một capability request, đã mã hoá JSON cùng tham số, không được vượt quá 262.144 byte. Request lớn hơn bị từ chối trước khi gửi bằng ValidationError, hoặc CogoverApiError có code: "FETCH_REQUEST_TOO_LARGE" với fetch; details.limit là giới hạn. Kết quả capability lớn hơn 1.048.576 byte cũng bị giữ lại theo cách đó (FETCH_RESPONSE_TOO_LARGE với fetch): hãy yêu cầu ít record hơn, hoặc nêu field thay cho "*". Bản thân thao tác vẫn đã chạy, nên thao tác ghi có thể đã hoàn tất.
  • Khi Workspace đã có quá nhiều lần thực thi đang chờ, lời gọi HTTP mới bị từ chối với HTTP 429 và code: "RATE_LIMITED" trước khi chạy bất cứ gì; hãy thử lại sau. Lỗi này không có details.budget.

Một số API còn có quota riêng cho mỗi lần thực thi, như 20 lời gọi fetch, 20 lần đọc secret, 20 lời gọi processes.start hoặc 5 lời gọi agents.start. Vượt các quota này cũng được báo bằng RateLimitError.

Đọc giới hạn trong code

const limits: LimitsApi;
 
interface LimitsApi {
  usage(): LimitUsage;
}
 
interface LimitUsage {
  readonly capabilityCalls: LimitCounter;
  readonly localCalls: LimitCounter;
  readonly recordsRead: LimitCounter;
  readonly recordsWritten: LimitCounter;
  readonly elapsedMs: number;
  readonly timeLimitMs: number;
}
 
interface LimitCounter {
  readonly used: number;
  readonly limit: number;
  readonly remaining: number;
}

limits được package export và cũng là thành phần limits trong context của script, route, record trigger, background job và Custom Module Action. limits.usage() trả các ngân sách của lần thực thi hiện tại tính đến lời gọi Cogover gần nhất: việc đọc không phải capability call. remaining là limit - used và không nhỏ hơn 0. elapsedMs là thời gian từ lúc handler bắt đầu tính đến lời gọi gần nhất đó, còn timeLimitMs là thời gian wall-clock lần thực thi được phép chạy. Gọi ngoài handler, ví dụ khi module đang nạp hoặc trong test chạy ngoài Cogover, usage() ném CogoverApiError với code: "CAPABILITY_UNAVAILABLE".

Khi code chạy local bằng Cogover Dev CLI, mỗi handler nhận ngân sách theo loại lần thực thi của nó và bị từ chối giống như trên Cogover; record liên kết mà expandLookups đọc không được tính ở local.

import { defineScript } from "@cogover/sdk";
 
export default defineScript(async ({ data, jobs, limits }) => {
  const orders = data.object("order");
  let cursor: string | undefined;
  let total = 0;
  do {
    const page = await orders.records.list({ fields: ["amount"], limit: 200, cursor });
    for (const order of page.items) total += Number(order.fields.amount ?? 0);
    cursor = page.nextCursor;
    const usage = limits.usage();
    if (cursor !== undefined && (usage.recordsRead.remaining < 200 || usage.capabilityCalls.remaining < 2)) {
      // Chuyển phần còn lại sang background job, vốn có ngân sách lớn hơn.
      await jobs.enqueue("sum_orders", { cursor, total });
      return { total, complete: false };
    }
  } while (cursor !== undefined);
  return { total, complete: true };
});

Với một số tổng như ví dụ này, records.aggregate chỉ tốn một capability call và không đọc record nào; chỉ duyệt từng trang record khi kết quả không biểu diễn được bằng aggregate.

Helper tương thích

function createGreeting(name: string): string;

createGreeting("Cogover") trả "Hello Cogover". Hàm được giữ để tương thích SDK 0.1.x và không thuộc Data API.

© 2026 Cogover LLC