Skip to main content
Функции логики — это серверные функции на TypeScript, которые выполняются на платформе Twenty. Их можно запускать HTTP-запросами, расписаниями cron или событиями базы данных — а также предоставлять как инструменты для ИИ-агентов.
Каждый файл функции использует 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.*
  • serverRoute: открывает один HTTP-маршрут в области регистрации. Функция-резолвер (объявленная с помощью serverRouteTriggerSettings) выполняется в рабочем пространстве-владельце и возвращает целевое рабочее пространство И целевую логическую функцию для маршрутизации; платформа затем запускает эту целевую функцию и возвращает ее ответ. См. триггер серверного маршрута.
Вы также можете вручную выполнить функцию с помощью CLI:
Вы можете просматривать логи с помощью:

Полезная нагрузка триггера маршрута

Когда триггер маршрута вызывает вашу логическую функцию, она получает объект RoutePayload, который соответствует формату AWS HTTP API v2. Импортируйте тип RoutePayload из twenty-sdk/logic-function:
Тип RoutePayload имеет следующую структуру:

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-type
  • content-language
  • content-disposition
  • cache-control
  • retry-after
Код состояния должен быть допустимым кодом состояния HTTP (в диапазоне от 100 до 599). Имена заголовков ответа сравниваются без учета регистра.

Триггер серверного маршрута

httpRouteTriggerSettings предоставляет функцию по пути /s/ и определяет рабочее пространство из хоста запроса — это работает, когда у каждого рабочего пространства свой домен. Поставщики сторонних сервисов, однако, отправляют события всех арендаторов на один URL. В этом случае используйте serverRouteTriggerSettings.Триггер состоит из двух частей:
  1. Логическая функция-резолвер — объявляется с помощью serverRouteTriggerSettings — выполняется в вашем рабочем пространстве-владельце (рабочем пространстве, которому принадлежит регистрация приложения). Она анализирует входящий запрос и возвращает { workspaceId, targetLogicFunctionUniversalIdentifier, payload? }, выбирая и целевое рабочее пространство, и целевую функцию. Резолвер является единой точкой авторизации — URL содержит только идентификатор резолвера. Это предпочтительное место для проверки подписей запросов: резолвер выполняется до любых побочных эффектов, имеет доступ к исходным rawBody и переадресованным заголовкам и может отклонить запрос, не обращаясь к целевой функции.
  2. Целевая логическая функция — обычная логическая функция на рабочее пространство — затем выполняется в определенном рабочем пространстве с полезной нагрузкой, возвращенной резолвером (или с исходной полезной нагрузкой запроса, если резолвер ее не преобразовал). Ее возвращаемое значение становится HTTP-ответом.
src/logic-functions/resolve-server-route.logic-function.ts
src/logic-functions/handle-invoice-paid.logic-function.ts
Конечная точка доступна по адресу:
Идентификатор — это universalIdentifier резолвера из вашего манифеста. Зарегистрируйте этот URL у поставщика.
Приложение должно быть закреплено и установлено в рабочем пространстве владельца. Поскольку резолвер выполняется в рабочем пространстве владельца (рабочем пространстве, которому принадлежит регистрация приложения), триггер серверного маршрута будет работать только после того, как приложение будет закреплено — то есть у него появится рабочее пространство владельца — и это приложение будет установлено в рабочем пространстве владельца. Пока оба этих условия не выполнены, резолверу негде выполняться, поэтому маршрут не может быть отправлен на обработку. Приложение, которое предоставляет логическую функцию serverRouteTriggerSettings, соответственно, не может быть размещено в маркетплейсе, пока оно не будет закреплено и установлено в рабочем пространстве владельца.
Контракт резолвера. Тип LogicFunctionConfig в SDK обеспечивает это на этапе компиляции: как только вы задаете serverRouteTriggerSettings, ваш обработчик обязан возвращать { workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object } (или Promise этого объекта). workspaceId должен указывать на рабочее пространство, в котором установлена целевая функция, иначе запрос будет отклонен с кодом 404.
Ответственность за проверку подписи лежит на вас — выполняйте проверку в резолвере. Платформа не проверяет подписи запросов. Резолвер — рекомендуемое место для этого: он выполняется первым, имеет доступ к event.rawBody и заголовкам, которые вы указали в forwardedRequestHeaders, и выброшенная ошибка (или любой workspaceId, не соответствующий ожидаемому) останавливает диспетчеризацию до вызова целевой функции. Если вместо этого вы перенесете проверку в целевую функцию, целевая функция должна позаботиться о том, чтобы не потерять rawBody и заголовки — то есть резолвер не должен возвращать payload. Всегда выполняйте проверку до любых побочных эффектов и используйте сравнение с постоянным временем выполнения.
Для подписей запросов большинство провайдеров используют HMAC-SHA256; различаются имя заголовка, кодировка дайджеста и строка подписываемой полезной нагрузки. Несколько примеров:Приведенный выше пример резолвера уже показывает поток GitHub HMAC-SHA256 — адаптируйте имя заголовка, кодировку дайджеста и строку подписываемой полезной нагрузки в соответствии с провайдером, с которым вы интегрируетесь.
Целевая функция выполняется синхронно, и ее возвращаемое значение становится HTTP-ответом, поэтому вызывающая сторона видит ваш статус-код и может повторить запрос при не-2xx коде. Делайте оба обработчика быстрыми — некоторые провайдеры (например, Slack) прерывают запрос через несколько секунд. Поскольку резолвер доступен как публичная конечная точка, защитите его с помощью ограничения частоты запросов (rate limiting) на вашем периметре (edge).

Полезная нагрузка триггера события базы данных

Когда триггер события базы данных вызывает вашу функцию логики, она получает по одному DatabaseEventPayload на каждую изменённую запись. Полезная нагрузка объединяет метаданные о рабочем пространстве-источнике и объекте с событием на уровне записи.
Полезная нагрузка включает:При логическом удалении .deleted имеет формат обновления, поскольку изменяется поле deletedAt записи. Для окончательного удаления используйте .destroyed.
databaseEventTriggerSettings.updatedFields фильтрует, какие события обновления запускают функцию. event.properties.updatedFields указывает, какие поля фактически изменились в текущем событии.
Пример события создания:
Пример события обновления:
Триггер только при обновлении email:
Пример события уничтожения:

Предоставление функции в качестве инструмента ИИ или действия рабочего процесса

Функции логики могут быть представлены в двух интерфейсах, у каждого — свой триггер:
  • toolTriggerSettings — делает функцию обнаруживаемой для возможностей ИИ Twenty (чат, MCP, вызов функций). Использует стандартную JSON Schema — формат, который модели LLM изначально понимают.
  • workflowActionTriggerSettings — делает функцию доступной как шаг в визуальном конструкторе рабочих процессов. Использует расширенную InputSchema от Twenty, чтобы конструктор мог отрисовывать корректные редакторы полей, селекторы переменных и подписи.
Функция может выбрать один, другой или оба варианта. Они идут рядом с cronTriggerSettings, databaseEventTriggerSettings и httpRouteTriggerSettings — тот же шаблон, та же структура.
Связь с действием Code рабочего процесса. Встроенное действие Code в конструкторе рабочих процессов само по себе является логической функцией — Twenty создаёт по одной на каждый шаг Code и отображает его редактор встроенным образом. workflowActionTriggerSettings — это способ превратить разовый встроенный код в повторно используемое действие: определите функцию один раз в своём приложении, и она станет доступной для выбора в любом рабочем процессе, вместо копирования и вставки в каждый шаг Code. См. действие Code в руководстве пользователя, чтобы увидеть, как это выглядит для конечного пользователя.
src/logic-functions/enrich-company.logic-function.ts
Основные моменты:
  • Функция может сочетать интерфейсы — объявите и toolTriggerSettings, и workflowActionTriggerSettings, чтобы сделать её доступной и в чате, и в конструкторе рабочих процессов.
  • toolTriggerSettings.inputSchema и workflowActionTriggerSettings.inputSchema — обе необязательны. Если они опущены, конструктор манифеста выводит их из исходного кода обработчика (JSON Schema — для инструмента ИИ, InputSchema от Twenty — для действия рабочего процесса). Укажите её явно, когда вам нужна более богатая типизация — например, с полями, учитывающими FieldMetadataType, такими как CURRENCY или RELATION, для конструктора рабочих процессов, или с полями description, которые может прочитать ИИ-агент:
Чтобы объявить параметры один раз и использовать их в обоих сценариях, определите одну JSON Schema (InputJsonSchema) и преобразуйте её для действия рабочего процесса с помощью jsonSchemaToInputSchema из twenty-sdk/logic-function. toolTriggerSettings.inputSchema принимает JSON Schema напрямую, в то время как workflowActionTriggerSettings.inputSchema ожидает InputSchema Twenty:
Полный пример действия рабочего процесса
workflowActionTriggerSettings принимает четыре поля:Объединяя всё вместе — функция, представленная как действие рабочего процесса, с объявленным выходом, чтобы последующие шаги могли ссылаться на taskId:
src/logic-functions/enrich-company.logic-function.ts
После установки приложения Enrich Company появляется в селекторе действий конструктора рабочих процессов. Конструктор отображает companyName и domain как поля ввода (каждое может получать значения из предыдущих шагов), а последующие шаги могут ссылаться на выходные значения шага taskId и enriched.
Напишите хорошее описание в поле 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 — основной клиент для запросов и изменений данных рабочего пространства. Он генерируется из схемы вашего рабочего пространства во время yarn twenty dev или yarn twenty dev:build, поэтому полностью типизирован в соответствии с вашими объектами и полями.
Клиент использует синтаксис selection-set: передайте true, чтобы включить поле, используйте __args для аргументов и вкладывайте объекты для отношений. Вы получаете полное автодополнение и проверку типов на основе схемы вашего рабочего пространства.
CoreApiClient генерируется на этапе dev/build. Если вы используете его, не запустив сначала yarn twenty dev или yarn twenty dev:build, он выбросит ошибку. Генерация происходит автоматически — CLI анализирует GraphQL-схему вашего рабочего пространства и создает типизированный клиент с помощью @genql/cli.

Использование CoreSchema для аннотаций типов

CoreSchema предоставляет типы TypeScript, соответствующие объектам вашего рабочего пространства — это полезно для типизации состояния компонентов или параметров функций:
MetadataApiClient поставляется в готовом виде вместе с SDK (генерация не требуется). Он выполняет запросы к эндпоинту /metadata для получения конфигурации рабочего пространства, приложений и загрузки файлов.

Загрузка файлов

MetadataApiClient включает метод uploadFile для прикрепления файлов к полям типа файла:
Основные моменты:
  • Он использует universalIdentifier поля (а не его идентификатор, специфичный для рабочего пространства), поэтому ваш код загрузки будет работать в любом рабочем пространстве, где установлено ваше приложение.
  • Возвращаемый url — это подписанный URL, который можно использовать для доступа к загруженному файлу.
Когда ваш код выполняется на Twenty (логические функции или фронт-компоненты), платформа предоставляет учётные данные в виде переменных окружения:
  • TWENTY_API_URL — базовый URL API Twenty
  • TWENTY_APP_ACCESS_TOKEN — краткоживущий ключ, ограниченный ролью функции по умолчанию вашего приложения
Вам не нужно передавать их клиентам — они автоматически читаются из process.env. Права ключа API определяются ролью, объявленной с помощью defineApplicationRole() (или указанной через defaultRoleUniversalIdentifier в application-config.ts).