defineLogicFunction
Définir des fonctions logiques et leurs déclencheurs
defineLogicFunction
Définir des fonctions logiques et leurs déclencheurs
Chaque fichier de fonction utilise Types de déclencheurs disponibles :Le type Dans votre gestionnaire, accédez aux en-têtes transférés comme ceci :Pour des raisons de sécurité, les en-têtes de réponse sont restreints à une liste d’autorisation. Tout en-tête qui ne figure pas dans la liste (par exemple Le point de terminaison est accessible à l’adresse :L’identifiant est le La vérification arrive dans Contrat du résolveur. Le type La charge utile inclut :Exemple d’événement de création :Exemple d’événement de mise à jour :Déclencher uniquement lors des mises à jour de l’adresse e-mail :Exemple d’événement de destruction :Créez les deux une seule fois, à la portée du module, et chaque site d’appel indique alors les accès qu’il utilise par le client qu’il appelle. Les assistants du SDK qui accèdent aux propres ressources de votre application utilisent toujours ses accès et ignorent Points clés :Pour déclarer vos paramètres une seule fois et les utiliser sur les deux surfaces, définissez un seul schéma JSON (Une fois l’application installée, Enrich Company apparaît dans le sélecteur d’actions du générateur de workflows. Le générateur affiche
defineLogicFunction() pour exporter une configuration avec un gestionnaire et des déclencheurs facultatifs.src/logic-functions/createPostCard.logic-function.ts
- httpRoute : Expose votre fonction sur un chemin et une méthode HTTP. Dans le code de l’application, préfixez le chemin de la route avec
/s/lorsque vous utilisezRestApiClient; l’URL déployée utilise la base injectéeTWENTY_FUNCTIONS_URL(ou\<server-url>/slorsqu’elle n’est pas définie).
Pour appeler une fonction logique déclenchée par une route depuis un composant frontal (sans interface), consultez Appeler une fonction logique.
- cron : Exécute votre fonction selon une planification à l’aide d’une expression CRON.
- databaseEvent: S’exécute lors des événements du cycle de vie des objets de l’espace de travail. Lorsque l’opération de l’événement est
updated, des champs spécifiques à surveiller peuvent être spécifiés dans le tableauupdatedFields. S’il est laissé indéfini ou vide, toute mise à jour déclenchera la fonction.
p. ex.person.updated,*.created,company.*
- serverRoute : expose une seule route HTTP à portée d’enregistrement. Une fonction de résolution (déclarée avec
serverRouteTriggerSettings) s’exécute dans l’espace de travail propriétaire et renvoie soit uneResponsesynchrone, soit l’espace de travail cible ET la fonction logique cible à mettre en file d’attente ; dans le cas de la mise en file d’attente, la plateforme accuse réception avec202et exécute cette cible dans la file d’attente du worker. Voir déclencheur de route serveur.
Vous pouvez également exécuter manuellement une fonction à l’aide de la CLI :Vous pouvez consulter les journaux avec :
Charge utile du déclencheur de route
Lorsqu’un déclencheur de route invoque votre fonction logique, elle reçoit un objetRoutePayload qui suit le
format AWS HTTP API v2.
Importez le type RoutePayload depuis twenty-sdk/logic-function :RoutePayload a la structure suivante :forwardedRequestHeaders
Par défaut, les en-têtes HTTP des requêtes entrantes ne sont pas transmis à votre fonction logique pour des raisons de sécurité. Pour accéder à des en-têtes spécifiques, listez-les dans le tableauforwardedRequestHeaders :Les noms d’en-têtes sont normalisés en minuscules. Accédez-y en utilisant des clés en minuscules (p. ex.,
event.headers['content-type']).Réponse HTTP personnalisée
Par défaut, le retour d’une valeur simple depuis votre gestionnaire l’envoie en réponse200 (JSON pour les objets, text/plain pour les chaînes). Pour contrôler le code d’état et les en-têtes de la réponse, retournez un objet Response depuis twenty-sdk/logic-function :Set-Cookie, les en-têtes CORS tels que Access-Control-Allow-Origin, ou les en-têtes personnalisés X-*) est silencieusement supprimé avant l’envoi de la réponse. Les en-têtes de réponse autorisés sont :content-typecontent-languagecontent-dispositioncache-controlretry-after
Le code d’état doit être un code d’état HTTP valide (compris entre 100 et 599). Les noms des en-têtes de réponse sont comparés sans tenir compte de la casse.
Réponses d’erreur de la plateforme
Au-delà des réponses propres à votre gestionnaire, la plateforme répond directement aux appels de route dans certaines situations :404 lorsque la route ou la fonction n’existe pas, 403 lorsque l’application est arrêtée, 429 lorsque la limite de débit d’exécution est atteinte, et 422 lorsque les dependencies de production de l’application sont trop volumineuses pour être installées — voir limites de taille des dépendances.Déclencheur de route serveur
httpRouteTriggerSettings expose une fonction sous /s/ et résout l’espace de travail à partir de l’hôte de la requête — ce qui fonctionne lorsque chaque espace de travail a son propre domaine. Les fournisseurs tiers, en revanche, envoient les événements de chaque locataire vers une URL. Dans ce cas, utilisez serverRouteTriggerSettings.Le déclencheur comporte deux parties :-
Une fonction de logique de résolution — déclarée avec
serverRouteTriggerSettings— s’exécute dans votre espace de travail propriétaire (l’espace de travail qui possède l’enregistrement de l’application). Elle inspecte la requête entrante et renvoie soit :{ workspaceId, targetLogicFunctionUniversalIdentifier, payload? }— la plateforme met cette cible en file d’attente dans l’espace de travail résolu et accuse réception avec202 { queued: true }, ou- une
Responsedetwenty-sdk/logic-function— la plateforme renvoie cette réponse HTTP de manière synchrone et ne met pas de cible en file d’attente (utilisez ceci pour les échanges de vérification, comme laurl_verificationde Slack).
rawBodyoriginal et aux en-têtes transmis, et peut rejeter la requête sans jamais toucher la cible. - Une fonction de logique cible — une fonction de logique classique par espace de travail — s’exécute ensuite dans l’espace de travail résolu avec la charge utile renvoyée par le résolveur (ou la charge utile originale de la requête si le résolveur ne l’a pas transformée). Sa valeur de retour n’est pas observée par l’appelant HTTP lorsque le résolveur a choisi le chemin de mise en file d’attente.
src/logic-functions/resolve-server-route.logic-function.ts
src/logic-functions/handle-invoice-paid.logic-function.ts
universalIdentifier du résolveur issu de votre manifeste. Enregistrez cette URL auprès du fournisseur.Répondre à une vérification GET. Certains fournisseurs vérifient un point de terminaison avant d’y effectuer une livraison, en envoyant une requête GET contenant une vérification à la même URL vers laquelle ils effectueront ensuite des événements POST — l’API WhatsApp Cloud de Meta en est un exemple. Une route de serveur répond uniquement à POST, sauf indication contraire. Déclarez donc les deux méthodes :event.queryStringParameters, et le renvoi d’une Response la renvoie au fournisseur dans la même requête. Un corps de chaîne est envoyé en tant que text/plain, ce que ces fournisseurs attendent :httpMethods remplace la valeur par défaut au lieu de s’y ajouter, donc ['GET'] seul fait que la route rejette POST. Seuls GET et POST sont pris en charge. Ne le définissez pas, sauf si le fournisseur a besoin du deuxième verbe : une route qui déclare GET verra son résolveur exécuté par tout appelant non authentifié, y compris les robots d’exploration et les outils de déploiement des aperçus de liens qui envoient des requêtes GET sans y être invités. Toute requête pour laquelle la plateforme ne dispose pas de méthode reçoit une réponse 405 sans que le résolveur ne soit jamais exécuté.L’application doit être revendiquée et installée sur son espace de travail propriétaire. Comme le résolveur s’exécute dans l’espace de travail propriétaire (l’espace de travail qui détient l’enregistrement de l’application), un déclencheur de route serveur ne fonctionne que lorsque l’application a été revendiquée — c’est‑à‑dire qu’elle possède un espace de travail propriétaire — et que cette application est installée sur l’espace de travail propriétaire. Tant que ces deux conditions ne sont pas remplies, le résolveur n’a nulle part où s’exécuter, donc la route ne peut pas être envoyée. Une application qui expose une fonction logique
serverRouteTriggerSettings ne peut donc pas être répertoriée sur la place de marché tant qu’elle n’a pas été revendiquée et installée sur son espace de travail propriétaire.LogicFunctionConfig du SDK impose cela à la compilation : dès que vous définissez serverRouteTriggerSettings, votre gestionnaire est contraint de retourner soit un Response, soit { workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object } (ou une Promise de l’un ou l’autre). Sur le chemin d’acheminement, le workspaceId doit être celui d’un espace de travail où la fonction cible est installée, sinon la requête est rejetée avec un 404. Un résultat qui ne correspond à aucune de ces formes — y compris un résultat dont les identifiants ne sont pas des UUID — est rejeté avec un 502.Pour les signatures de requêtes, la plupart des fournisseurs signent avec HMAC-SHA256 ; les éléments qui diffèrent sont le nom de l’en-tête, l’encodage de l’empreinte et la chaîne de la charge utile signée. Quelques exemples :
L’exemple de résolveur ci-dessus montre déjà le flux GitHub HMAC-SHA256 — adaptez le nom de l’en-tête, l’encodage de l’empreinte et la chaîne de la charge utile signée en fonction du fournisseur avec lequel vous vous intégrez.
Lorsque le résolveur retourne un objet d’acheminement, la route répond
202 { queued: true } et la cible s’exécute dans la file d’attente du worker — l’appelant n’observe jamais la latence, le résultat ou les échecs de la cible (ceux-ci sont enregistrés dans les journaux d’exécution). Cela évite que les nouvelles tentatives d’envoi de l’émetteur n’amplifient les ralentissements de traitement, ce qui est souhaitable pour l’ingestion de webhook.Lorsque l’appelant doit lire le corps de la réponse sur la même requête (handshakes de challenge, accusés de réception interactifs), retournez plutôt un Response depuis le résolveur. La plateforme le renvoie de manière synchrone et ignore la file d’attente ; ses en-têtes passent par la même liste d’autorisation que les réponses des routes HTTP. Gardez le résolveur rapide — certains fournisseurs (par ex. Slack) ont un délai d’attente de seulement quelques secondes. Comme le résolveur est accessible en tant que point de terminaison public, protégez-le avec une limitation de débit à votre périphérie.Charge utile du déclencheur d’événement de base de données
Lorsqu’un déclencheur d’événement de base de données appelle votre fonction logique, celle-ci reçoit unDatabaseEventPayload par enregistrement modifié. La charge utile combine les métadonnées concernant l’espace de travail et l’objet source avec l’événement au niveau de l’enregistrement.Pour les suppressions logiques (soft deletes),
.deleted suit la structure de type mise à jour, car le champ deletedAt de l’enregistrement change.
Pour les suppressions permanentes, utilisez .destroyed.databaseEventTriggerSettings.updatedFields filtre les événements de mise à jour qui déclenchent la fonction.
event.properties.updatedFields indique quels champs ont réellement changé pour l’événement actuel.Contexte d’exécution
Chaque gestionnaire reçoit un deuxième argument décrivant l’exécution elle-même, quel que soit ce qui l’a déclenchée. Alors que la forme du premier argument varie selon le déclencheur, celle-ci ne varie pas :userWorkspaceId et workspaceMemberId sont null lorsque personne n’a déclenché l’exécution : les planifications cron, les hooks d’installation et les webhooks non authentifiés n’ont aucune personne derrière eux. Ils sont également null lorsque la personne ne possède aucun enregistrement de membre de l’espace de travail, ou que le sien a été supprimé.Le contexte vous indique qui a déclenché l’exécution. Ce que l’exécution peut faire est une déclaration distincte, ci-dessous.
Quels accès un appel utilise
Chaque client —CoreApiClient, MetadataApiClient et RestApiClient — agit en tant que personne ayant déclenché l’exécution : son rôle est combiné à celui de votre application, de sorte que l’appel ne peut jamais faire plus que l’un ou l’autre. C’est le comportement par défaut, et cela signifie qu’une personne ne peut jamais utiliser votre application pour dépasser ses propres autorisations.Lorsque personne n’a déclenché l’exécution, il n’y a aucune personne dont il faut adopter l’identité, le même client utilise donc les propres accès de votre application : les planifications cron, les hooks d’installation et les webhooks non authentifiés ne nécessitent aucun traitement particulier.Certains appels nécessitent légitimement les propres accès de l’application même lorsqu’une personne est à l’origine de l’exécution — pour lire les enregistrements de configuration de votre application, ou effectuer un travail que cette personne ne pourrait pas faire elle-même. Créez un deuxième client pour ceux-ci :RestApiClient accepte la même option :Une exécution que personne n’a déclenchée agit en tant que votre application. Les planifications cron, les hooks d’installation et les webhooks non authentifiés n’ont aucune personne derrière eux, le client par défaut utilise donc les propres accès de votre application et continue de fonctionner.
runAs: 'application' n’est nécessaire que lorsque vous souhaitez utiliser ces accès dans une exécution qu’une personne a déclenchée.Vérifiez context.workspaceMemberId lorsqu’une fonction se comporte différemment selon qu’une personne est derrière elle, par exemple pour attribuer un enregistrement.runAs : le magasin clé-valeur, les connexions, runAgent, getPublicAssetUrl et la facturation de crédits.Exposer une fonction en tant qu’outil d’IA ou en tant qu’action de workflow
Les fonctions logiques peuvent être exposées sur deux surfaces, chacune avec son propre déclencheur :toolTriggerSettings— rend la fonction découvrable par les fonctionnalités d’IA de Twenty (chat, MCP, appel de fonctions). Utilise le schéma JSON standard, le format que les LLM comprennent nativement.workflowActionTriggerSettings— fait apparaître la fonction comme une étape dans le concepteur visuel de workflows. Utilise leInputSchemariche de Twenty afin que le concepteur puisse afficher des éditeurs de champs appropriés, des sélecteurs de variables et des libellés.
cronTriggerSettings, databaseEventTriggerSettings et httpRouteTriggerSettings — même modèle, même structure.Lien avec l’action Code du workflow. L’action Code intégrée dans le générateur de workflows est elle-même une fonction logique — Twenty en crée une pour chaque étape Code et affiche son éditeur en ligne.
workflowActionTriggerSettings est la manière de transformer ce code ponctuel en ligne en une action réutilisable : définissez la fonction une fois dans votre application et elle devient sélectionnable dans n’importe quel workflow, au lieu d’être copiée-collée dans chaque étape Code. Voir l’action Code dans le guide utilisateur pour la vue côté utilisateur final.src/logic-functions/enrich-company.logic-function.ts
- Une fonction peut mélanger les surfaces — déclarez à la fois
toolTriggerSettingsetworkflowActionTriggerSettingspour l’exposer à la fois dans le chat ET dans le concepteur de workflows. toolTriggerSettings.inputSchemaetworkflowActionTriggerSettings.inputSchemasont tous deux facultatifs. Lorsqu’ils sont omis, le générateur de manifeste les déduit à partir du code source du gestionnaire (schéma JSON pour l’outil d’IA,InputSchemade Twenty pour l’action de workflow). Fournissez-en un explicitement lorsque vous souhaitez un typage plus riche — par exemple, avec des champs compatibles avecFieldMetadataTypecommeCURRENCYouRELATIONpour le concepteur de workflows, ou avec des champsdescriptionque l’agent d’IA peut lire :
InputJsonSchema) et convertissez-le pour l’action de flux de travail avec jsonSchemaToInputSchema depuis twenty-sdk/logic-function. toolTriggerSettings.inputSchema prend directement le schéma JSON, tandis que workflowActionTriggerSettings.inputSchema attend le InputSchema de Twenty :Un exemple complet d’action de workflow
workflowActionTriggerSettings accepte quatre champs :Assembler le tout — une fonction exposée comme action de workflow, avec une sortie déclarée pour que les étapes ultérieures puissent référencer
taskId :src/logic-functions/enrich-company.logic-function.ts
companyName et domain sous forme de champs de saisie (chacun pouvant récupérer des valeurs à partir des étapes précédentes), et les étapes en aval peuvent référencer les sorties taskId et enriched de l’étape.Rédigez une bonne
description. Les agents IA s’appuient sur le champ description de la fonction pour décider quand utiliser l’outil. Soyez précis sur ce que fait l’outil et quand il doit être appelé.Aides à l’exécution.
twenty-sdk/utils réexporte de petites aides à l’exécution afin que les gestionnaires n’importent jamais directement depuis twenty-shared. Par exemple, isDefined(value) renvoie false à la fois pour null et undefined — utilisez-le pour restreindre en toute sécurité les entrées de gestionnaire optionnelles, qui peuvent arriver sous forme de null à l’exécution même lorsqu’elles sont typées T | undefined :Hooks d’installation — les gestionnaires de pré-installation, de post-installation et de désinstallation — partagent ce runtime mais sont déclarés avec leurs propres fonctions define et ne prennent pas de paramètres de déclenchement. Voir hooks d’installation pour
definePreInstallLogicFunction, definePostInstallLogicFunction et defineUninstallLogicFunction.Créer une activité de chronologie.
UtilisezcreateTimelineActivity() pour publier un événement de domaine explicite depuis une fonction logique. Définissez d’abord l’événement comme un type d’activité de chronologie, puis référencez le type et les objets à l’aide de leurs identifiants universels stables :
timelineActivityTypeUniversalIdentifier, targetObjectUniversalIdentifier et targetRecordId. Vous pouvez également fournir happensAt, properties et workspaceMemberId. happensAt contrôle la position et l’heure affichée de l’événement dans la chronologie ; par défaut, il correspond à l’heure de création.
Pour associer un autre enregistrement à l’événement, fournissez ensemble linkedRecordId et linkedObjectMetadataUniversalIdentifier. Vous pouvez également fournir linkedRecordCachedName comme solution de repli d’affichage historique :
timelineActivity. Gardez les types d’événements explicites non liés à une action ; un type lié à une action reçoit déjà des événements d’audit automatiques et produirait sinon des lignes en double.
Clients d’API typés (twenty-client-sdk)
Le packagetwenty-client-sdk fournit deux clients GraphQL typés pour interagir avec l’API Twenty depuis vos fonctions logiques et vos composants frontaux.
CoreApiClient
Interroger et modifier les données de l'espace de travail (enregistrements, objets)
CoreApiClient
Interroger et modifier les données de l'espace de travail (enregistrements, objets)
CoreApiClient est le client principal pour interroger et modifier les données de l’espace de travail. Il est généré à partir du schéma de votre espace de travail lors de l’exécution de yarn twenty dev ou yarn twenty dev:build, il est donc entièrement typé pour correspondre à vos objets et champs.true pour inclure un champ, utilisez __args pour les arguments et imbriquez des objets pour les relations. Vous bénéficiez d’une autocomplétion complète et d’une vérification de types basée sur le schéma de votre espace de travail.CoreApiClient est généré au moment du dev/build. Si vous l’utilisez sans exécuter d’abord
yarn twenty dev ou yarn twenty dev:build, une erreur est levée. La génération se fait automatiquement — la CLI inspecte le schéma GraphQL de votre espace de travail et génère un client typé à l’aide de @genql/cli.Utiliser CoreSchema pour les annotations de type
CoreSchema fournit des types TypeScript correspondant à vos objets d’espace de travail — utile pour typer l’état des composants ou les paramètres de fonction :MetadataApiClient
Configuration de l'espace de travail, applications et téléversements de fichiers
MetadataApiClient
Configuration de l'espace de travail, applications et téléversements de fichiers
MetadataApiClient est livré prêt à l’emploi avec le SDK (aucune génération requise). Il interroge le point de terminaison /metadata pour la configuration de l’espace de travail, les applications et les téléversements de fichiers. Il accepte la même option runAs que CoreApiClient — voir Quels accès un appel utilise.Téléverser des fichiers
LeMetadataApiClient inclut une méthode uploadFile pour joindre des fichiers aux champs de type fichier :Points clés :
- Utilise le
universalIdentifierdu champ (et non son ID propre à l’espace de travail), de sorte que votre code de téléversement fonctionne dans tout espace de travail où votre application est installée. - L’
urlrenvoyée est une URL signée que vous pouvez utiliser pour accéder au fichier téléversé.
Lorsque votre code s’exécute sur Twenty (fonctions logiques ou composants frontaux), la plateforme injecte des identifiants sous forme de variables d’environnement :
TWENTY_API_URL— URL de base de l’API TwentyTWENTY_APP_ACCESS_TOKEN— Clé de courte durée pour l’accès par défaut : le rôle d’une personne combiné à celui de votre application lorsqu’une personne est à l’origine de l’exécution, le propre rôle de votre application lorsque personne ne l’est. La personne est celle qui a déclenché l’exécution dans une fonction logique, ou celle qui consulte la page dans un composant frontal.TWENTY_APP_APPLICATION_ACCESS_TOKEN— Clé de courte durée limitée au seul rôle de votre application. Uniquement pour les fonctions logiques, toujours injectée dans celles-ci, et utilisée parrunAs: 'application'.
process.env, et Quels accès un appel utilise explique comment choisir entre eux. Les propres autorisations de votre application sont déterminées par le rôle déclaré avec defineApplicationRole() (ou référencé via defaultRoleUniversalIdentifier dans application-config.ts) ; une exécution agissant en tant que personne ne peut jamais dépasser ce rôle, ni le sien.