defineLogicFunction
Define funciones de lógica y sus desencadenadores
defineLogicFunction
Define funciones de lógica y sus desencadenadores
Cada archivo de función usa Tipos de desencadenadores disponibles:El tipo En tu controlador, accede a los encabezados reenviados así:Por razones de seguridad, los encabezados de la respuesta están restringidos a una lista de permitidos. Cualquier encabezado que no esté en la lista (por ejemplo, El endpoint es accesible en:El identificador es el El desafío llega en Contrato del resolver. El tipo La carga útil incluye:Ejemplo de evento de creación:Ejemplo de evento de actualización:Ejecutar solo en actualizaciones de correo electrónico:Ejemplo de evento de eliminación:Crea ambos una vez, en el ámbito del módulo, y cada sitio de llamada indica entonces qué acceso utiliza según el cliente al que llama. Los सहायantes del SDK que acceden a los recursos propios de tu aplicación siempre usan su acceso e ignoran Puntos clave:Para declarar tus parámetros una sola vez y atender ambas superficies, define un único JSON Schema (Una vez que la aplicación está instalada, Enrich Company aparece en el selector de acciones del generador de flujos de trabajo. El generador representa
defineLogicFunction() para exportar una configuración con un controlador y desencadenadores opcionales.src/logic-functions/createPostCard.logic-function.ts
- httpRoute: Expone tu función en una ruta y método HTTP. En el código de la aplicación, añade el prefijo
/s/a la ruta cuando usesRestApiClient; la URL desplegada utiliza la base inyectadaTWENTY_FUNCTIONS_URL(o\<server-url>/scuando no está configurada).
Para invocar una función de lógica activada por una ruta desde un componente de frontend (headless), consulta Llamar a una función de lógica.
- cron: Ejecuta tu función en un horario usando una expresión CRON.
- databaseEvent: Se ejecuta en eventos del ciclo de vida de objetos del espacio de trabajo. Cuando la operación del evento es
updated, se pueden especificar campos específicos que se deben escuchar en la matrizupdatedFields. Si se deja sin definir o vacío, cualquier actualización activará la función.
p. ej.person.updated,*.created,company.*
- serverRoute: expone una única ruta HTTP con ámbito de registro. Una función de resolver (declarada con
serverRouteTriggerSettings) se ejecuta en el espacio de trabajo propietario y devuelve unaResponsesíncrona o el espacio de trabajo de destino Y la función de lógica de destino para poner en cola; en la ruta de encolado, la plataforma confirma con202y ejecuta ese destino en la cola de workers. Consulta Disparador de ruta de servidor.
También puedes ejecutar manualmente una función usando la CLI:Puedes ver los registros con:
Carga útil del disparador de ruta
Cuando un desencadenador de ruta invoca tu función de lógica, esta recibe un objetoRoutePayload que sigue el
formato AWS HTTP API v2.
Importa el tipo RoutePayload desde twenty-sdk/logic-function:RoutePayload tiene la siguiente estructura:forwardedRequestHeaders
De forma predeterminada, los encabezados HTTP de las solicitudes entrantes no se pasan a tu función de lógica por razones de seguridad. Para acceder a encabezados específicos, enuméralos explícitamente en el arregloforwardedRequestHeaders:Los nombres de los encabezados se normalizan a minúsculas. Accede a ellos usando claves en minúsculas (p. ej.,
event.headers['content-type']).Respuesta HTTP personalizada
De forma predeterminada, devolver un valor sencillo desde tu controlador lo envía de vuelta como una respuesta200 (JSON para objetos, text/plain para cadenas). Para controlar el código de estado y los encabezados de la respuesta, devuelve un Response desde twenty-sdk/logic-function:Set-Cookie, encabezados CORS como Access-Control-Allow-Origin, o encabezados personalizados X-*) se descarta silenciosamente antes de que se envíe la respuesta. Los encabezados de respuesta permitidos son:content-typecontent-languagecontent-dispositioncache-controlretry-after
El código de estado debe ser un código de estado HTTP válido (entre 100 y 599). Los nombres de los encabezados de respuesta se comparan sin distinguir mayúsculas de minúsculas.
Respuestas de error de la plataforma
Más allá de las propias respuestas de tu controlador, la plataforma responde directamente a las llamadas de ruta en algunas situaciones:404 cuando la ruta o la función no existe, 403 cuando la aplicación está detenida, 429 cuando se alcanza el límite de velocidad de ejecución y 422 cuando las dependencies de producción de la aplicación son demasiado grandes para instalarlas; consulta límites de tamaño de dependencias.Disparador de ruta de servidor
httpRouteTriggerSettings expone una función bajo /s/ y resuelve el espacio de trabajo a partir del host de la solicitud — lo cual funciona cuando cada espacio de trabajo tiene su propio dominio. Los proveedores de terceros, sin embargo, entregan los eventos de cada inquilino a una URL. Para ese caso, usa serverRouteTriggerSettings.El disparador tiene dos partes:-
Una función de lógica de resolver — declarada con
serverRouteTriggerSettings— se ejecuta en tu espacio de trabajo propietario (el espacio de trabajo que es propietario del registro de la aplicación). Inspecciona la solicitud entrante y devuelve una de las siguientes opciones:{ workspaceId, targetLogicFunctionUniversalIdentifier, payload? }— la plataforma pone en cola ese destino en el espacio de trabajo resuelto y confirma con202 { queued: true }, o- un
Responsedetwenty-sdk/logic-function— la plataforma replica esa respuesta HTTP de forma sincrónica y no pone en cola un destino (usa esto para desafíos de verificación comourl_verificationde Slack).
rawBodyoriginal y a los encabezados reenviados, y puede rechazar sin tocar nunca el destino. - Luego, una función de lógica de destino — una función de lógica normal por espacio de trabajo — se ejecuta en el espacio de trabajo resuelto con el payload devuelto por el resolver (o el payload original de la solicitud si el resolver no lo transformó). Su valor de retorno no es observado por el solicitante HTTP cuando el resolver eligió la ruta de encolado.
src/logic-functions/resolve-server-route.logic-function.ts
src/logic-functions/handle-invoice-paid.logic-function.ts
universalIdentifier del resolver de tu manifiesto. Registra esa URL con el proveedor.Responder a un GET de verificación. Algunos proveedores verifican un punto de conexión antes de entregar contenido en él, enviando un GET con un desafío a la misma URL a la que más tarde enviarán eventos mediante POST; la API de WhatsApp Cloud de Meta es uno de ellos. Una ruta de servidor responde solo a POST, a menos que indiques lo contrario, así que declara ambos métodos:event.queryStringParameters, y devolver una Response lo reenvía al proveedor en la misma solicitud. Un cuerpo de cadena se envía como text/plain, que es lo que esperan estos proveedores:httpMethods reemplaza el valor predeterminado en lugar de añadirse a él, por lo que ['GET'] por sí solo hace que la ruta rechace POST. Solo se admiten GET y POST. Déjalo sin configurar a menos que el proveedor necesite el segundo verbo: una ruta que declara GET tendrá su resolver ejecutado por cualquier llamador no autenticado, incluidos los rastreadores y los desplegadores de enlaces que envían GET sin solicitarlo. Cualquier solicitud para la que la plataforma no tenga un método se responde con 405 sin que el resolver llegue a ejecutarse.La aplicación debe reclamarse e instalarse en el espacio de trabajo propietario. Dado que el resolver se ejecuta en el espacio de trabajo propietario (el espacio de trabajo que es propietario del registro de la aplicación), un desencadenador de ruta de servidor solo funciona una vez que la aplicación ha sido reclamada, es decir, tiene un espacio de trabajo propietario, y esa aplicación está instalada en el espacio de trabajo propietario. Hasta que ambas condiciones se cumplan, el resolver no tiene dónde ejecutarse, por lo que la ruta no puede despacharse. Por lo tanto, una aplicación que expone una función lógica
serverRouteTriggerSettings no puede figurar en el marketplace hasta que haya sido reclamada e instalada en su espacio de trabajo propietario.LogicFunctionConfig del SDK aplica esto en tiempo de compilación: tan pronto como configuras serverRouteTriggerSettings, tu handler se ve obligado a devolver un Response, o { workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object } (o un Promise de cualquiera de los dos). En la ruta de envío, el workspaceId debe ser un espacio de trabajo donde la función de destino esté instalada; de lo contrario, la solicitud se rechaza con 404. Un resultado que no coincide con ninguna de las dos formas —incluido uno cuyos identificadores no sean UUID— se rechaza con 502.Para las firmas de solicitudes, la mayoría de los proveedores firman con HMAC-SHA256; las partes que difieren son el nombre del encabezado, la codificación del digest y la cadena firmada del payload. Algunos ejemplos:
El ejemplo de resolver anterior ya muestra el flujo HMAC-SHA256 de GitHub; adapta el nombre del encabezado, la codificación del digest y la cadena firmada del payload según el proveedor con el que te estés integrando.
Cuando el resolver devuelve un objeto de envío, la ruta responde
202 { queued: true } y el destino se ejecuta en la cola de workers: el solicitante nunca observa la latencia, el resultado ni los fallos del destino (estos se registran en los registros de ejecución). Esto evita que los reintentos del remitente amplifiquen las ralentizaciones del procesamiento, que es lo que se desea para la ingesta de webhooks.Cuando el solicitante deba leer el cuerpo de la respuesta en la misma solicitud (desafíos de verificación, acuses de recibo interactivos), en su lugar devuelve un Response desde el resolver. La plataforma lo replica de forma síncrona y omite la cola; sus cabeceras pasan por la misma lista de permitidos que las respuestas de rutas HTTP. Mantén el resolver rápido: algunos proveedores (p. ej. Slack) agotan el tiempo de espera en pocos segundos. Como el resolver es accesible como un endpoint público, protégelo con limitación de tasa en el edge.Payload del disparador de evento de base de datos
Cuando un disparador de evento de base de datos invoca tu función de lógica, esta recibe unDatabaseEventPayload por cada registro modificado. El payload combina metadatos sobre el espacio de trabajo y el objeto de origen con el evento a nivel de registro.Para eliminaciones lógicas (soft deletes),
.deleted sigue la estructura de estilo de actualización porque el campo deletedAt del registro cambia.
Para eliminaciones permanentes, usa .destroyed.databaseEventTriggerSettings.updatedFields filtra qué eventos de actualización activan la función.
event.properties.updatedFields te indica qué campos realmente cambiaron en el evento actual.Contexto de ejecución
Cada controlador recibe un segundo argumento que describe la propia ejecución, independientemente de lo que la haya desencadenado. Mientras que el primer argumento cambia de forma según el desencadenante, este no:userWorkspaceId y workspaceMemberId son null cuando nadie desencadenó la ejecución: las programaciones cron, los hooks de instalación y los webhooks no autenticados no tienen a ninguna persona detrás. También son null cuando la persona no tiene un registro de miembro del espacio de trabajo, o cuando el suyo se ha eliminado.El contexto te indica quién desencadenó la ejecución. Lo que la ejecución puede hacer es una declaración independiente, a continuación.
Qué acceso utiliza una llamada
Cada cliente —CoreApiClient, MetadataApiClient y RestApiClient — actúa como la persona que desencadenó la ejecución: su rol se intersecta con el de tu aplicación, por lo que la llamada nunca puede hacer más de lo que cualquiera de los dos puede hacer. Ese es el comportamiento predeterminado y significa que una persona nunca puede usar tu aplicación para superar sus propios permisos.Cuando nadie desencadenó la ejecución, no hay ninguna persona como la que actuar, por lo que el mismo cliente recurre al acceso propio de tu aplicación: las programaciones cron, los hooks de instalación y los webhooks no autenticados no requieren ninguna gestión especial.Algunas llamadas necesitan legítimamente el acceso propio de la aplicación incluso cuando hay una persona detrás de la ejecución — leer los registros de configuración de tu aplicación o realizar trabajo que esa persona no podría hacer por sí misma. Crea un segundo cliente para esos casos:RestApiClient acepta la misma opción:Una ejecución que nadie desencadenó actúa como tu aplicación. Las programaciones cron, los hooks de instalación y los webhooks no autenticados no tienen a ninguna persona detrás, por lo que el cliente predeterminado recurre al acceso propio de tu aplicación y sigue funcionando.
runAs: 'application' solo se necesita cuando quieres ese acceso en una ejecución que una persona sí desencadenó.Comprueba context.workspaceMemberId cuando una función se comporta de forma diferente según haya alguien detrás de ella, por ejemplo, para atribuir un registro.runAs: el almacén de clave-valor, las conexiones, runAgent, getPublicAssetUrl y el cobro de créditos.Exponer una función como herramienta de IA o acción de flujo de trabajo
Las funciones lógicas pueden exponerse en dos ámbitos, cada uno con su propio disparador:toolTriggerSettings— hace que la función sea descubrible por las funciones de IA de Twenty (chat, MCP, llamadas a funciones). Usa el JSON Schema estándar, el formato que los LLM entienden de forma nativa.workflowActionTriggerSettings— hace que la función aparezca como un paso en el constructor visual de flujos de trabajo. Usa elInputSchemacompleto de Twenty para que el constructor pueda renderizar editores de campos adecuados, selectores de variables y etiquetas.
cronTriggerSettings, databaseEventTriggerSettings y httpRouteTriggerSettings — mismo patrón, misma estructura.Relación con la acción Code del flujo de trabajo. La acción integrada Code en el generador de flujos de trabajo es en sí misma una función lógica: Twenty crea una por cada paso de Code y expone su editor en línea.
workflowActionTriggerSettings es la forma de convertir ese código puntual en línea en una acción reutilizable: defines la función una vez en tu aplicación y se vuelve seleccionable en cualquier flujo de trabajo, en lugar de copiarla y pegarla en cada paso de Code. Consulta la acción Code en la guía del usuario para ver la perspectiva del usuario final.src/logic-functions/enrich-company.logic-function.ts
- Una función puede mezclar superficies — declara tanto
toolTriggerSettingscomoworkflowActionTriggerSettingspara exponerla en el chat Y en el constructor de flujos de trabajo. - Ambos,
toolTriggerSettings.inputSchemayworkflowActionTriggerSettings.inputSchema, son opcionales. Cuando se omiten, el generador del manifiesto los infiere a partir del código fuente del controlador (JSON Schema para la herramienta de IA,InputSchemade Twenty para la acción de flujo de trabajo). Proporciona uno explícitamente cuando quieras un tipado más rico — por ejemplo, con campos compatibles conFieldMetadataTypecomoCURRENCYoRELATIONpara el constructor de flujos de trabajo, o con camposdescriptionque el agente de IA pueda leer:
InputJsonSchema) y conviértelo para la acción de flujo de trabajo con jsonSchemaToInputSchema de twenty-sdk/logic-function. toolTriggerSettings.inputSchema usa directamente el JSON Schema, mientras que workflowActionTriggerSettings.inputSchema espera el InputSchema de Twenty:Ejemplo completo de una acción de flujo de trabajo
workflowActionTriggerSettings acepta cuatro campos:Uniéndolo todo: una función expuesta como una acción de flujo de trabajo, con una salida declarada para que los pasos posteriores puedan hacer referencia a
taskId:src/logic-functions/enrich-company.logic-function.ts
companyName y domain como campos de entrada (cada uno puede extraer valores de pasos anteriores), y los pasos posteriores pueden hacer referencia a las salidas taskId y enriched de ese paso.Escribe una buena
description. Los agentes de IA dependen del campo description de la función para decidir cuándo usar la herramienta. Sé específico acerca de lo que hace la herramienta y cuándo debe invocarse.Utilidades en tiempo de ejecución.
twenty-sdk/utils vuelve a exportar pequeñas utilidades en tiempo de ejecución para que los handlers nunca importen directamente desde twenty-shared. Por ejemplo, isDefined(value) devuelve false tanto para null como para undefined — utilízalo para acotar de forma segura las entradas opcionales de los handlers, que pueden llegar como null en tiempo de ejecución incluso cuando están tipadas como T | undefined:Hooks de instalación — los controladores de preinstalación, postinstalación y desinstalación — comparten este entorno de ejecución, pero se declaran con sus propias funciones
define y no aceptan configuraciones de disparador. Consulta Hooks de instalación para definePreInstallLogicFunction, definePostInstallLogicFunction y defineUninstallLogicFunction.Crear una actividad de línea de tiempo.
UsecreateTimelineActivity() para publicar un evento explícito del dominio desde una función lógica. Primero defina el evento como un tipo de actividad de línea de tiempo y, a continuación, dirija el tipo y los objetos mediante sus identificadores universales estables:
timelineActivityTypeUniversalIdentifier, targetObjectUniversalIdentifier y targetRecordId. También puede proporcionar happensAt, properties y workspaceMemberId. happensAt controla la posición y la hora mostrada del evento en la línea de tiempo; de forma predeterminada, corresponde a la hora de creación.
Para asociar otro registro con el evento, proporcione linkedRecordId y linkedObjectMetadataUniversalIdentifier juntos. Además, puede proporcionar linkedRecordCachedName como alternativa histórica de visualización:
timelineActivity. Mantenga los tipos de eventos explícitos sin vincular a una action; un tipo vinculado a una acción ya recibe eventos de auditoría automáticos y, de lo contrario, produciría filas duplicadas.
Clientes de API tipados (twenty-client-sdk)
El paquetetwenty-client-sdk proporciona dos clientes GraphQL tipados para interactuar con la API de Twenty desde tus funciones de lógica y componentes de frontend.
CoreApiClient
Consultar y modificar datos del espacio de trabajo (registros, objetos)
CoreApiClient
Consultar y modificar datos del espacio de trabajo (registros, objetos)
CoreApiClient es el cliente principal para consultar y mutar datos del espacio de trabajo. Se genera a partir del esquema de tu espacio de trabajo durante yarn twenty dev o yarn twenty dev:build, por lo que está completamente tipado para coincidir con tus objetos y campos.true para incluir un campo, usa __args para los argumentos y anida objetos para las relaciones. Obtienes autocompletado completo y verificación de tipos basados en el esquema de tu espacio de trabajo.CoreApiClient se genera en tiempo de desarrollo/compilación. Si intentas usarlo sin ejecutar primero
yarn twenty dev o yarn twenty dev:build, lanzará un error. La generación ocurre automáticamente: la CLI inspecciona el esquema GraphQL de tu espacio de trabajo y genera un cliente tipado usando @genql/cli.Uso de CoreSchema para anotaciones de tipos
CoreSchema proporciona tipos de TypeScript que coinciden con los objetos de tu espacio de trabajo; útil para tipar el estado de componentes o parámetros de funciones:MetadataApiClient
Configuración del espacio de trabajo, aplicaciones y cargas de archivos
MetadataApiClient
Configuración del espacio de trabajo, aplicaciones y cargas de archivos
MetadataApiClient viene preconstruido con el SDK (no se requiere generación). Consulta el endpoint /metadata para la configuración del espacio de trabajo, las aplicaciones y las cargas de archivos. Acepta la misma opción runAs que CoreApiClient — consulta Qué acceso utiliza una llamada.Subir archivos
ElMetadataApiClient incluye un método uploadFile para adjuntar archivos a los campos de tipo archivo:Puntos clave:
- Utiliza el
universalIdentifierdel campo (no su ID específico del espacio de trabajo), por lo que tu código de carga funciona en cualquier espacio de trabajo donde esté instalada tu aplicación. - La
urldevuelta es una URL firmada que puedes usar para acceder al archivo cargado.
Cuando tu código se ejecuta en Twenty (funciones de lógica o componentes de frontend), la plataforma inyecta credenciales como variables de entorno:
TWENTY_API_URL— URL base de la API de TwentyTWENTY_APP_ACCESS_TOKEN— Clave de corta duración para el acceso predeterminado: el rol de una persona se intersecta con el de tu aplicación cuando hay alguien detrás de la ejecución; el rol propio de tu aplicación cuando no hay nadie. La persona es quien haya desencadenado la ejecución en una función de lógica, o quien esté viendo la página en un componente de frontend.TWENTY_APP_APPLICATION_ACCESS_TOKEN— Clave de corta duración con alcance únicamente al rol propio de tu aplicación. Solo funciones de lógica, siempre se inyecta allí, y es lo que utilizarunAs: 'application'.
process.env automáticamente, y Qué acceso utiliza una llamada explica cómo elegir entre ellas. Los permisos propios de tu aplicación están determinados por el rol declarado con defineApplicationRole() (o referenciado mediante defaultRoleUniversalIdentifier en application-config.ts); una ejecución que actúa como una persona nunca puede superar ese rol ni el suyo.