defineLogicFunction
定义逻辑函数及其触发器
defineLogicFunction
定义逻辑函数及其触发器
每个函数文件都使用 可用的触发器类型:
在你的处理程序中,可以这样访问被转发的请求头:出于安全原因,响应头被限制在一个允许列表中。 任何不在该列表中的响应头(例如 该函数可在以下地址访问:这两个标识符都是来自清单的
解析得到的值必须是有效的 workspace UUID,并且 你的应用必须已安装在该 workspace 中,否则请求会在函数运行前被拒绝。大多数服务商使用 HMAC-SHA256 进行签名;不同之处在于请求头名称、摘要编码方式以及被签名的负载字符串。 例如:
有效负载包括:
对于软删除,创建事件示例:更新事件示例:仅在 email 更新时触发:销毁事件示例:关键点:要只声明一次参数并服务于这两种界面,请定义一个单个 JSON Schema(
defineLogicFunction() 导出包含处理程序和可选触发器的配置。src/logic-functions/createPostCard.logic-function.ts
- httpRoute:在
/s/端点下通过 HTTP 路径和方法公开你的函数:
例如path: '/post-card/create'可在https://your-twenty-server.com/s/post-card/create调用
要从(无头)前端组件调用由路由触发的逻辑函数,请参见调用逻辑函数。
- cron:使用 CRON 表达式按计划运行你的函数。
- databaseEvent:在工作区对象生命周期事件上运行。 当事件操作为
updated时,可以在updatedFields数组中指定要监听的特定字段。 如果未定义或为空,任何更新都会触发该函数。
例如person.updated、*.created、company.*
- serverWebhook:从第三方服务(Stripe、GitHub、Svix 等)接收入站 Webhooks 在单个以注册为作用域的端点上,并从有效负载中解析目标工作区。 参见 服务端 Webhook 触发器。
你也可以使用 CLI 手动执行函数:你可以通过以下方式查看日志:
路由触发器负载
当路由触发器调用你的逻辑函数时,它会接收一个遵循 AWS HTTP API v2 格式的RoutePayload 对象。
从 twenty-sdk/logic-function 导入 RoutePayload 类型:RoutePayload 类型具有以下结构:| 属性 | 类型 | 描述 | 示例 |
|---|---|---|---|
headers | Record\<string, string | undefined> | HTTP 请求头(仅限 forwardedRequestHeaders 中列出的那些) | 见下文 |
queryStringParameters | Record\<string, string | undefined> | 查询字符串参数(多个值以逗号连接) | /users?ids=1&ids=2&ids=3&name=Alice -> { ids: '1,2,3', name: 'Alice' } |
pathParameters | Record\<string, string | undefined> | 从路由模式中提取的路径参数 | /users/:id,/users/123 -> { id: '123' } |
body | object | null | 已解析的请求体(JSON) | { id: 1 } -> { id: 1 } |
rawBody | string | undefined | 在 JSON 解析之前的原始 UTF-8 请求体。 用于验证 HMAC 风格的 Webhook 签名(例如 GitHub 的 X-Hub-Signature-256、Stripe)。 当运行时未保留它时为 undefined。 | |
isBase64Encoded | boolean | 请求体是否为 base64 编码 | |
requestContext.http.method | string | HTTP 方法(GET、POST、PUT、PATCH、DELETE) | |
requestContext.http.path | string | 原始请求路径 |
forwardedRequestHeaders
出于安全原因,默认不会将传入请求的 HTTP 请求头传递给你的逻辑函数。 如需访问特定请求头,请在forwardedRequestHeaders 数组中显式列出:请求头名称会被规范化为小写。 请使用小写键访问它们(例如,
event.headers['content-type'])。自定义 HTTP 响应
默认情况下,从处理程序返回一个普通值会以200 响应返回该值(对象为 JSON,字符串为 text/plain)。 要控制状态码和响应头,请从 twenty-sdk/logic-function 返回一个 Response:Set-Cookie、CORS 响应头(如 Access-Control-Allow-Origin),或自定义的 X-* 响应头)都会在发送响应之前被静默丢弃。 允许的响应头包括:content-typecontent-languagecontent-dispositioncache-controlretry-after
状态码必须是有效的 HTTP 状态码(介于 100 和 599 之间)。 响应头名称的匹配不区分大小写。
服务端 Webhook 触发器
httpRouteTriggerSettings 在 /s/ 下暴露一个函数,并根据请求主机解析 workspace——这在每个 workspace 都有自己域名时有效。 然而,第三方服务商会将每个租户的事件发送到同一个 webhook URL。 对于这种情况,请使用 serverWebhookTriggerSettings:该函数可在注册范围的端点访问,并且 workspace 将从负载中解析。src/logic-functions/handle-provider-webhook.logic-function.ts
universalIdentifier——即应用注册的 universalIdentifier 和此逻辑函数的 universalIdentifier。 在服务商处注册该 URL。Workspace 解析。 由于一个端点为每个 workspace 提供服务,你的集成必须在传递内容中的某处放入目标 workspaceId,而 workspaceIdResolver.{ source, path } 用于告知平台从哪里读取它:| 字段 | 值 | 备注 |
|---|---|---|
来源 | body | query | header | body 读取解析后的 JSON。 query 是最通用的——通常你可以控制所注册的回调 URL,因此可以追加 ?twentyWorkspaceId=…。 |
路径 | 点路径,例如 metadata.twentyWorkspaceId | 仅限字母数字 / _ / - 片段;原型键将被拒绝。 |
| 提供商 | 要转发的请求头 | 签名字符串 | 摘要 |
|---|---|---|---|
| Svix(Recall、Resend、Clerk) | webhook-id, webhook-timestamp, webhook-signature | {id}.{timestamp}.{rawBody} | base64(密钥在去掉 whsec_ 前缀后为 base64) |
| Stripe | stripe-signature | {timestamp}.{rawBody} | hex |
| GitHub | x-hub-signature-256 | {rawBody} | hex(前缀为 sha256=) |
| Shopify | x-shopify-hmac-sha256 | {rawBody} | base64 |
| Slack | x-slack-signature, x-slack-request-timestamp | v0:{timestamp}:{rawBody} | hex(前缀为 v0=) |
该函数同步运行,你返回的值会成为 HTTP 响应,因此服务商可以看到你的状态码,并在非 2xx 时重试。 保持处理程序足够快速——某些服务商(例如 Slack)会在几秒内超时。 由于函数在检查签名前运行,请在边缘通过速率限制来保护此端点。
数据库事件触发器有效负载
当数据库事件触发器调用你的逻辑函数时,每条被更改的记录都会对应一个DatabaseEventPayload。 该负载将关于源工作区和对象的元数据与记录级事件组合在一起。| 属性 | 描述 |
|---|---|
name | 事件名称,例如 person.updated。 |
workspaceId | 事件发生的工作区。 |
objectMetadata | 已更改对象的元数据。 |
recordId | 已更改记录的 ID。 |
userId, userWorkspaceId, workspaceMemberId | 当事件由工作区用户触发时的操作者字段。 |
properties | 事件的记录数据,根据操作不同,包含 before、after、diff 和 updatedFields。 |
| 事件 | 记录数据 |
|---|---|
person.created | event.properties.after |
person.updated | event.properties.before, event.properties.after, event.properties.diff, event.properties.updatedFields |
person.destroyed | event.properties.before |
.deleted 遵循更新样式的结构,因为记录的 deletedAt 字段发生了变化。
对于永久删除,请使用 .destroyed。databaseEventTriggerSettings.updatedFields 会筛选出哪些更新事件会触发该函数。
event.properties.updatedFields 告诉你在当前事件中哪些字段实际发生了变化。将函数公开为 AI 工具或工作流操作
逻辑函数可以在两个入口对外公开,每个入口都有各自的触发器:toolTriggerSettings— 使该函数可被 Twenty 的 AI 功能(chat、MCP、function calling)发现。 使用标准 JSON Schema,LLM 能够原生理解的格式。workflowActionTriggerSettings— 使该函数在可视化工作流构建器中显示为一个步骤。 使用 Twenty 丰富的InputSchema,以便构建器可以呈现合适的字段编辑器、变量选择器和标签。
cronTriggerSettings、databaseEventTriggerSettings 和 httpRouteTriggerSettings 并列 — 相同的模式、相同的结构。src/logic-functions/enrich-company.logic-function.ts
- 函数可以混用这些入口 — 同时声明
toolTriggerSettings和workflowActionTriggerSettings,即可在 chat 和工作流构建器中同时公开它。 toolTriggerSettings.inputSchema和workflowActionTriggerSettings.inputSchema均为可选。 如果省略,清单构建器会根据处理器源代码进行推断(AI 工具使用 JSON Schema,工作流操作使用 Twenty 的InputSchema)。 当你需要更丰富的类型时,可显式提供一个 — 例如,在工作流构建器中使用对FieldMetadataType友好的字段(如CURRENCY或RELATION),或提供 AI 智能体可读取的description字段:
InputJsonSchema),并使用来自 twenty-sdk/logic-function 的 jsonSchemaToInputSchema 将其转换为工作流操作所用。 toolTriggerSettings.inputSchema 直接接受 JSON Schema,而 workflowActionTriggerSettings.inputSchema 需要的是 Twenty 的 InputSchema:写一个好的
description。 AI 智能体会依赖该函数的 description 字段来决定何时使用该工具。 明确说明该工具的作用以及应在何时调用。运行时辅助工具。
twenty-sdk/utils 会重新导出一些小型运行时辅助工具,这样处理程序就不需要直接从 twenty-shared 导入。 例如,isDefined(value) 对 null 和 undefined 都会返回 false —— 使用它可以安全地收窄可选处理程序输入的类型,因为即使类型标注为 T | undefined,在运行时它们仍可能以 null 的形式传入:安装 hooks——预安装和后安装处理程序——共享此运行时,但使用它们自己的 define 函数进行声明,并且不接受触发器设置。 有关
definePreInstallLogicFunction 和 definePostInstallLogicFunction,请参阅 Install Hooks。类型化 API 客户端(twenty-client-sdk)
twenty-client-sdk 包提供了两个类型化的 GraphQL 客户端,供你的逻辑函数和前端组件与 Twenty API 交互。
| 客户端 | 导入 | 端点 | 是否生成? |
|---|---|---|---|
CoreApiClient | twenty-client-sdk/core | /graphql——工作区数据(记录、对象) | 是,在开发/构建时 |
MetadataApiClient | twenty-client-sdk/metadata | /metadata——工作区配置、文件上传 | 否,已预构建提供 |
CoreApiClient
查询和变更工作区数据(记录、对象)
CoreApiClient
查询和变更工作区数据(记录、对象)
CoreApiClient 是用于查询和变更工作区数据的主要客户端。 它会在执行 yarn twenty dev 或 yarn twenty dev:build 时根据你的工作区架构生成,因此具有完整的类型定义以匹配你的对象和字段。true 以包含某字段,使用 __args 传递参数,并通过嵌套对象表示关系。 你将基于工作区架构获得完整的自动补全和类型检查。CoreApiClient 在开发/构建时生成。 如果在未先运行
yarn twenty dev 或 yarn twenty dev:build 的情况下尝试使用它,将会抛出错误。 该生成过程是自动完成的——CLI 会自省你的工作区 GraphQL 架构,并使用 @genql/cli 生成类型化客户端。使用 CoreSchema 进行类型标注
CoreSchema 提供与工作区对象相匹配的 TypeScript 类型,可用于为组件状态或函数参数进行类型标注:MetadataApiClient
工作区配置、应用和文件上传
MetadataApiClient
工作区配置、应用和文件上传
MetadataApiClient 随 SDK 一并提供,已预构建(无需生成)。 它会查询 /metadata 端点以获取工作区配置、应用和文件上传。上传文件
MetadataApiClient 包含一个 uploadFile 方法,用于将文件附加到文件类型字段:| 参数 | 类型 | 描述 |
|---|---|---|
fileBuffer | Buffer | 原始文件内容 |
filename | string | 文件名称(用于存储和显示) |
contentType | string | MIME 类型(如果省略,默认为 application/octet-stream) |
fieldMetadataUniversalIdentifier | string | 你的对象上文件类型字段的 universalIdentifier |
- 使用字段的
universalIdentifier(而不是其工作区特定的 ID),因此你的上传代码可在安装了你的应用的任何工作区中运行。 - 返回的
url是一个签名 URL,你可以用它来访问已上传的文件。
当你的代码在 Twenty 上运行(逻辑函数或前端组件)时,平台会以环境变量的形式注入凭据:
TWENTY_API_URL——Twenty API 的基础 URLTWENTY_APP_ACCESS_TOKEN——作用域限定为你的应用默认函数角色的短期密钥
process.env 读取。 API 密钥的权限由使用 defineApplicationRole() 声明的角色(或在 application-config.ts 中通过 defaultRoleUniversalIdentifier 引用的角色)决定。