defineLogicFunction
Определяйте логические функции и их триггеры
defineLogicFunction
Определяйте логические функции и их триггеры
Каждый файл функции использует Доступные типы триггеров:Тип
В обработчике обращайтесь к переданным заголовкам следующим образом:По соображениям безопасности заголовки ответа ограничены списком разрешенных заголовков. Любой заголовок, которого нет в этом списке (например, Функция доступна по адресу:Оба идентификатора — это
Определённое значение должно быть действительным UUID рабочего пространства, и ваше приложение должно быть установлено в этом рабочем пространстве, иначе запрос будет отклонён до запуска функции.Большинство провайдеров подписывают с помощью HMAC-SHA256; различаются имя заголовка, кодировка дайджеста и строка подписываемой полезной нагрузки. Несколько примеров:
Полезная нагрузка включает:
При логическом удалении Пример события создания:Пример события обновления:Триггер только при обновлении email:Пример события уничтожения:Основные моменты:Чтобы объявить параметры один раз и использовать их в обоих сценариях, определите одну JSON Schema (
defineLogicFunction() для экспорта конфигурации с обработчиком и необязательными триггерами.src/logic-functions/createPostCard.logic-function.ts
- httpRoute: Публикует вашу функцию по HTTP-пути и методу под конечной точкой
/s/:
например,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, …) на единственной конечной точке в области регистрации и определяет целевое рабочее пространство из полезной нагрузки. См. триггер серверного вебхука.
Вы также можете вручную выполнить функцию с помощью CLI:Вы можете просматривать логи с помощью:
Полезная нагрузка триггера маршрута
Когда триггер маршрута вызывает вашу логическую функцию, она получает объектRoutePayload, который соответствует формату AWS HTTP API v2.
Импортируйте тип RoutePayload из twenty-sdk/logic-function: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 | Исходное тело запроса в кодировке UTF-8, до разбора JSON. Полезно для проверки подписей вебхуков в стиле HMAC (например, X-Hub-Signature-256 от GitHub, 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 для строк). Чтобы управлять статус-кодом и заголовками ответа, верните Response из twenty-sdk/logic-function:Set-Cookie, CORS-заголовки, такие как Access-Control-Allow-Origin, или пользовательские заголовки X-*), молчаливо удаляется перед отправкой ответа. Разрешенные заголовки ответа:content-typecontent-languagecontent-dispositioncache-controlretry-after
Код состояния должен быть допустимым кодом состояния HTTP (в диапазоне от 100 до 599). Имена заголовков ответа сравниваются без учета регистра.
Серверный триггер вебхука
httpRouteTriggerSettings предоставляет функцию по пути /s/ и определяет рабочее пространство из хоста запроса — это работает, когда у каждого рабочего пространства свой домен. Поставщики сторонних сервисов, однако, отправляют события всех арендаторов на один URL вебхука. В этом случае используйте serverWebhookTriggerSettings: функция доступна на конечной точке в области регистрации, а рабочее пространство определяется из полезной нагрузки.src/logic-functions/handle-provider-webhook.logic-function.ts
universalIdentifier из вашего манифеста: регистрации приложения и этой логической функции. Зарегистрируйте этот URL у поставщика.Определение рабочего пространства. Поскольку одна конечная точка обслуживает все рабочие пространства, ваша интеграция должна поместить целевой workspaceId в передаваемые данные, а workspaceIdResolver.{ source, path } указывает платформе, откуда его прочитать:| Поле | Значения | Заметки |
|---|---|---|
source | body | query | header | body читает разобранный JSON. query — самый универсальный вариант: вы обычно контролируете URL обратного вызова, который регистрируете, поэтому добавьте ?twentyWorkspaceId=…. |
path | точечный путь, например metadata.twentyWorkspaceId | Ограничено сегментами из буквенно-цифровых символов / _ / -; ключи прототипа отклоняются. |
| Провайдер | Заголовки для пересылки | Подписываемая строка | Дайджест |
|---|---|---|---|
| Svix (Recall, Resend, Clerk) | webhook-id, webhook-timestamp, webhook-signature | {id}.{timestamp}.{rawBody} | base64 (секрет в формате base64 после удаления префикса whsec_) |
| Stripe | stripe-signature | {timestamp}.{rawBody} | hex |
| GitHub | x-hub-signature-256 | {rawBody} | hex (с префиксом sha256=) |
| Shopify | x-shopify-hmac-sha256 | {rawBody} | base64 |
| Слэк | x-slack-signature, x-slack-request-timestamp | v0:{timestamp}:{rawBody} | hex (с префиксом v0=) |
Функция выполняется синхронно, и возвращаемое вами значение становится HTTP-ответом, поэтому провайдеры видят ваш статус-код и могут повторить запрос при не-2xx коде. Делайте обработчики быстрыми — некоторые провайдеры (например, Slack) прерывают запрос через несколько секунд. Поскольку функция выполняется до проверки подписи, защитите эту конечную точку ограничением частоты на периметре (edge).
Полезная нагрузка триггера события базы данных
Когда триггер события базы данных вызывает вашу функцию логики, она получает по одномуDatabaseEventPayload на каждую изменённую запись. Полезная нагрузка объединяет метаданные о рабочем пространстве-источнике и объекте с событием на уровне записи.| Свойство | Описание |
|---|---|
name | Имя события, например person.updated. |
workspaceId | Рабочее пространство, в котором произошло событие. |
objectMetadata | Метаданные для объекта, который изменился. |
recordId | Идентификатор измененной записи. |
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 указывает, какие поля фактически изменились в текущем событии.Предоставление функции в качестве инструмента ИИ или действия рабочего процесса
Функции логики могут быть представлены в двух интерфейсах, у каждого — свой триггер:toolTriggerSettings— делает функцию обнаруживаемой для возможностей ИИ Twenty (чат, MCP, вызов функций). Использует стандартную JSON Schema — формат, который модели LLM изначально понимают.workflowActionTriggerSettings— делает функцию доступной как шаг в визуальном конструкторе рабочих процессов. Использует расширеннуюInputSchemaот Twenty, чтобы конструктор мог отрисовывать корректные редакторы полей, селекторы переменных и подписи.
cronTriggerSettings, databaseEventTriggerSettings и httpRouteTriggerSettings — тот же шаблон, та же структура.src/logic-functions/enrich-company.logic-function.ts
- Функция может сочетать интерфейсы — объявите и
toolTriggerSettings, иworkflowActionTriggerSettings, чтобы сделать её доступной и в чате, и в конструкторе рабочих процессов. toolTriggerSettings.inputSchemaиworkflowActionTriggerSettings.inputSchema— обе необязательны. Если они опущены, конструктор манифеста выводит их из исходного кода обработчика (JSON Schema — для инструмента ИИ,InputSchemaот Twenty — для действия рабочего процесса). Укажите её явно, когда вам нужна более богатая типизация — например, с полями, учитывающимиFieldMetadataType, такими какCURRENCYилиRELATION, для конструктора рабочих процессов, или с полямиdescription, которые может прочитать ИИ-агент:
InputJsonSchema) и преобразуйте её для действия рабочего процесса с помощью jsonSchemaToInputSchema из twenty-sdk/logic-function. toolTriggerSettings.inputSchema принимает JSON Schema напрямую, в то время как workflowActionTriggerSettings.inputSchema ожидает InputSchema Twenty:Напишите хорошее описание в поле
description. Агенты ИИ опираются на поле description функции, чтобы решить, когда использовать инструмент. Чётко опишите, что делает инструмент и когда его следует вызывать.Вспомогательные функции времени выполнения.
twenty-sdk/utils повторно экспортирует небольшие вспомогательные функции времени выполнения, поэтому обработчики никогда не импортируют напрямую из twenty-shared. Например, isDefined(value) возвращает false как для null, так и для undefined — используйте её, чтобы безопасно сузить необязательные входные данные обработчика, которые могут приходить как null во время выполнения, даже если имеют тип T | undefined:Хуки установки — обработчики до установки и после установки — используют тот же рантайм, но объявляются с помощью собственных функций
define и не принимают настройки триггеров. См. раздел Install Hooks для definePreInstallLogicFunction и definePostInstallLogicFunction.Типизированные клиенты API (twenty-client-sdk)
Пакетtwenty-client-sdk предоставляет два типизированных клиента GraphQL для взаимодействия с API Twenty из ваших логических функций и фронт-компонентов.
| Клиент | Импорт | Конечная точка | Генерируется? |
|---|---|---|---|
CoreApiClient | twenty-client-sdk/core | /graphql — данные рабочего пространства (записи, объекты) | Да, на этапе dev/build |
MetadataApiClient | twenty-client-sdk/metadata | /metadata — конфигурация рабочего пространства, загрузка файлов | Нет, поставляется в готовом виде |
CoreApiClient
Запрос и изменение данных рабочего пространства (записи, объекты)
CoreApiClient
Запрос и изменение данных рабочего пространства (записи, объекты)
CoreApiClient — основной клиент для запросов и изменений данных рабочего пространства. Он генерируется из схемы вашего рабочего пространства во время yarn twenty dev или yarn twenty dev:build, поэтому полностью типизирован в соответствии с вашими объектами и полями.true, чтобы включить поле, используйте __args для аргументов и вкладывайте объекты для отношений. Вы получаете полное автодополнение и проверку типов на основе схемы вашего рабочего пространства.CoreApiClient генерируется на этапе dev/build. Если вы используете его, не запустив сначала
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поля (а не его идентификатор, специфичный для рабочего пространства), поэтому ваш код загрузки будет работать в любом рабочем пространстве, где установлено ваше приложение. - Возвращаемый
url— это подписанный URL, который можно использовать для доступа к загруженному файлу.
Когда ваш код выполняется на Twenty (логические функции или фронт-компоненты), платформа предоставляет учётные данные в виде переменных окружения:
TWENTY_API_URL— базовый URL API TwentyTWENTY_APP_ACCESS_TOKEN— краткоживущий ключ, ограниченный ролью функции по умолчанию вашего приложения
process.env. Права ключа API определяются ролью, объявленной с помощью defineApplicationRole() (или указанной через defaultRoleUniversalIdentifier в application-config.ts).