Skip to main content
Logikfunktionen sind serverseitige TypeScript-Funktionen, die auf der Twenty-Plattform ausgeführt werden. Sie können durch HTTP-Anfragen, cron-Zeitpläne oder Datenbankereignisse ausgelöst werden — und außerdem als Tools für KI-Agenten bereitgestellt werden.
Jede Funktionsdatei verwendet defineLogicFunction(), um eine Konfiguration mit einem Handler und optionalen Triggern zu exportieren.
src/logic-functions/createPostCard.logic-function.ts
Verfügbare Trigger-Typen:
  • httpRoute: Stellt Ihre Funktion über einen HTTP-Pfad und eine Methode bereit. Im Anwendungscode den Routenpfad bei Verwendung von RestApiClient mit dem Präfix /s/ versehen; die bereitgestellte URL verwendet als Basis die injizierte TWENTY_FUNCTIONS_URL (oder \<server-url>/s, wenn diese nicht gesetzt ist).
Um eine routenausgelöste Logikfunktion von einer (headless) Front-Komponente aus aufzurufen, siehe Aufrufen einer Logikfunktion.
  • cron: Führt Ihre Funktion nach Zeitplan mithilfe eines CRON-Ausdrucks aus.
  • databaseEvent: Wird bei Lebenszyklusereignissen von Workspace-Objekten ausgeführt. Wenn die Ereignisoperation updated ist, können bestimmte zu überwachende Felder im Array updatedFields angegeben werden. Wenn das Array undefiniert oder leer ist, löst jede Aktualisierung die Funktion aus.
z. B. person.updated, *.created, company.*
  • serverRoute: Stellt eine einzelne, registrierungsbezogene HTTP-Route bereit. Eine Resolver-Funktion (deklariert mit serverRouteTriggerSettings) läuft im Owner-Workspace und gibt entweder eine synchrone Response zurück oder den Ziel-Workspace UND die Ziel-Logikfunktion, die in die Queue eingereiht werden soll; auf dem Enqueue-Pfad bestätigt die Plattform mit 202 und führt dieses Ziel in der Worker-Queue aus. Siehe Server-Route-Trigger.
Sie können eine Funktion auch manuell über die CLI ausführen:
Sie können Protokolle mit folgendem Befehl ansehen:

Routen-Trigger-Payload

Wenn ein Route-Trigger Ihre Logikfunktion aufruft, erhält sie ein RoutePayload-Objekt, das dem AWS-HTTP-API-v2-Format folgt. Importieren Sie den Typ RoutePayload aus twenty-sdk/logic-function:
Der Typ RoutePayload hat die folgende Struktur:

forwardedRequestHeaders

Standardmäßig werden HTTP-Header von eingehenden Anfragen aus Sicherheitsgründen nicht an Ihre Logikfunktion weitergegeben. Um auf bestimmte Header zuzugreifen, listen Sie diese im Array forwardedRequestHeaders auf:
Greifen Sie in Ihrem Handler wie folgt auf die weitergeleiteten Header zu:
Header-Namen werden in Kleinbuchstaben normalisiert. Greifen Sie mit Schlüsseln in Kleinbuchstaben darauf zu (z. B. event.headers['content-type']).

Benutzerdefinierte HTTP-Antwort

Standardmäßig sendet das Zurückgeben eines einfachen Werts aus Ihrem Handler diesen als 200-Antwort zurück (JSON für Objekte, text/plain für Zeichenketten). Um den Statuscode und die Antwort-Header zu steuern, geben Sie eine Response aus twenty-sdk/logic-function zurück:
Aus Sicherheitsgründen sind Antwort-Header auf eine Allowlist beschränkt. Jeder Header, der nicht auf der Liste steht (z. B. Set-Cookie, CORS-Header wie Access-Control-Allow-Origin oder benutzerdefinierte X-*-Header), wird stillschweigend verworfen, bevor die Antwort gesendet wird. Die erlaubten Antwort-Header sind:
  • content-type
  • content-language
  • content-disposition
  • cache-control
  • retry-after
Der Statuscode muss ein gültiger HTTP-Statuscode sein (zwischen 100 und 599). Antwort-Header-Namen werden ohne Beachtung der Groß-/Kleinschreibung verglichen.

Server-Route-Trigger

httpRouteTriggerSettings stellt eine Funktion unter /s/ bereit und ermittelt den Workspace aus dem Host der Anfrage — was funktioniert, wenn jeder Workspace seine eigene Domain hat. Drittanbieter hingegen liefern die Ereignisse jedes Mandanten an eine URL. Für diesen Fall verwende serverRouteTriggerSettings.Der Trigger besteht aus zwei Teilen:
  1. Eine Resolver-Logikfunktion – deklariert mit serverRouteTriggerSettings – läuft in deinem Owner-Workspace (dem Workspace, dem die Anwendungsregistrierung gehört). Sie inspiziert die eingehende Anfrage und gibt entweder Folgendes zurück:
    • { workspaceId, targetLogicFunctionUniversalIdentifier, payload? } — die Plattform reiht dieses Ziel im aufgelösten Workspace in die Queue ein und bestätigt mit 202 { queued: true }, oder
    • eine Response von twenty-sdk/logic-function — die Plattform gibt diese HTTP-Response synchron zurück und reiht kein Ziel in die Queue ein (verwende dies für Challenge-Handshakes wie Slack url_verification).
    Der Resolver ist die einzige Autorisierungsstelle – die URL enthält nur den Bezeichner des Resolvers. Dies ist der bevorzugte Ort, um Anfragesignaturen zu verifizieren: Der Resolver läuft vor jeglicher Nebenwirkung, hat Zugriff auf den ursprünglichen rawBody und weitergeleitete Header und kann ablehnen, ohne jemals das Ziel zu berühren.
  2. Eine Target-Logikfunktion – eine reguläre, Workspace-spezifische Logikfunktion – läuft dann im aufgelösten Workspace mit dem Payload, der vom Resolver zurückgegeben wurde (oder dem ursprünglichen Anfrage-Payload, falls der Resolver ihn nicht transformiert hat). Sein Rückgabewert wird vom HTTP-Aufrufer nicht beobachtet, wenn der Resolver den Enqueue-Pfad gewählt hat.
src/logic-functions/resolve-server-route.logic-function.ts
src/logic-functions/handle-invoice-paid.logic-function.ts
Der Endpoint ist erreichbar unter:
Der Bezeichner ist der universalIdentifier des Resolvers aus deinem Manifest. Registriere diese URL beim Provider.
Die Anwendung muss beansprucht und im Arbeitsbereich ihres Besitzers installiert werden. Da der Resolver im Owner-Arbeitsbereich ausgeführt wird (dem Arbeitsbereich, der die Anwendungsregistrierung besitzt), funktioniert ein Server-Route-Trigger nur, wenn die Anwendung beansprucht wurde – d. h. sie einen Owner-Arbeitsbereich hat – und diese Anwendung im Owner-Arbeitsbereich installiert ist. Solange beides nicht zutrifft, hat der Resolver keinen Ausführungsort, sodass die Route nicht ausgeführt werden kann. Eine Anwendung, die eine serverRouteTriggerSettings-Logikfunktion bereitstellt, kann daher nicht im Marketplace aufgeführt werden, bevor sie beansprucht und im Owner-Arbeitsbereich installiert wurde.
Resolver-Vertrag. Der LogicFunctionConfig-Typ des SDK erzwingt dies zur Compile-Zeit: Sobald du serverRouteTriggerSettings setzt, ist dein Handler darauf beschränkt, entweder eine Response oder { workspaceId: string; targetLogicFunctionUniversalIdentifier: string; payload?: object } zurückzugeben (oder ein Promise von einem der beiden). Auf dem Dispatch-Pfad muss die workspaceId auf einen Workspace verweisen, in dem die Zielfunktion installiert ist, andernfalls wird die Anfrage mit 404 abgelehnt. Ein Ergebnis, das keiner der beiden Formen entspricht – einschließlich eines Ergebnisses, dessen Bezeichner keine UUIDs sind – wird mit 502 abgelehnt.
Die Signaturverifizierung liegt in deiner Verantwortung – verifiziere im Resolver. Die Plattform verifiziert Anfragesignaturen nicht. Der Resolver ist der empfohlene Ort dafür: Er läuft zuerst, mit Zugriff auf event.rawBody und die Header, die du in forwardedRequestHeaders aufgeführt hast, und ein geworfener Fehler (oder eine nicht passende workspaceId) stoppt das Dispatching, bevor das Target aufgerufen wird. Wenn du die Verifizierung stattdessen in das Target verschiebst, muss das Target darauf achten, rawBody und Header nicht zu verlieren – d. h. der Resolver darf keinen payload zurückgeben. Überprüfe immer vor jeglicher Nebenwirkung und verwende einen Vergleich in konstanter Zeit.
Für Anfragesignaturen signieren die meisten Provider mit HMAC-SHA256; die Teile, die sich unterscheiden, sind der Header-Name, die Digest-Codierung und der signierte Payload-String. Einige Beispiele:Das obige Resolver-Beispiel zeigt bereits den GitHub-HMAC-SHA256-Flow – passe den Header-Namen, die Digest-Codierung und den signierten Payload-String entsprechend dem Provider an, den du integrierst.
Wenn der Resolver ein Dispatch-Objekt zurückgibt, antwortet die Route mit 202 { queued: true } und das Ziel wird in der Worker-Queue ausgeführt – der Aufrufer bekommt weder die Latenz, noch das Ergebnis oder Fehler des Ziels mit (diese werden in den Ausführungsprotokollen aufgezeichnet). Dadurch wird verhindert, dass erneute Zustellungen des Senders Verarbeitungsverzögerungen verstärken – genau das ist bei der Erfassung von Webhooks erwünscht.Wenn der Aufrufer den Response-Body in derselben Anfrage lesen muss (Challenge-Handshakes, interaktive Bestätigungen), gib stattdessen eine Response aus dem Resolver zurück. Die Plattform gibt sie synchron zurück und überspringt die Queue; ihre Header durchlaufen dieselbe Allow-Liste wie HTTP-Routen-Antworten. Halten Sie den Resolver schnell – einige Provider (z. B. Slack) laufen nach wenigen Sekunden in ein Timeout. Da der Resolver als öffentlicher Endpoint erreichbar ist, schütze ihn mit Rate-Limiting an deinem Edge.

Datenbank-Event-Trigger-Payload

Wenn ein Datenbank-Event-Trigger Ihre Logic Function aufruft, erhält sie eine DatabaseEventPayload pro geändertem Datensatz. Die Payload kombiniert Metadaten über den Quell-Workspace und das Objekt mit dem Ereignis auf Datensatzebene.
Die Nutzlast umfasst:Bei Soft Deletes folgt .deleted der Aktualisierungsstruktur, da sich das Feld deletedAt des Datensatzes ändert. Für dauerhafte Löschvorgänge verwende .destroyed.
databaseEventTriggerSettings.updatedFields filtert, welche Aktualisierungsereignisse die Funktion auslösen. event.properties.updatedFields gibt an, welche Felder beim aktuellen Ereignis tatsächlich geändert wurden.
Beispiel für ein Created-Ereignis:
Beispiel für ein Updated-Ereignis:
Nur bei E-Mail-Aktualisierungen auslösen:
Beispiel für ein Destroyed-Ereignis:

Eine Funktion als KI-Tool oder Workflow-Aktion verfügbar machen

Logikfunktionen können auf zwei Oberflächen verfügbar gemacht werden, jeweils mit eigenem Trigger:
  • toolTriggerSettings — macht die Funktion über die KI-Funktionen von Twenty (Chat, MCP, Funktionsaufrufe) auffindbar. Verwendet das standardmäßige JSON Schema, das Format, das LLMs nativ verstehen.
  • workflowActionTriggerSettings — lässt die Funktion als Schritt im visuellen Workflow-Builder erscheinen. Verwendet das umfangreiche InputSchema von Twenty, sodass der Builder geeignete Feldeditoren, Variablenauswahlen und Beschriftungen rendern kann.
Eine Funktion kann sich für eine, die andere oder beide entscheiden. Sie stehen neben cronTriggerSettings, databaseEventTriggerSettings und httpRouteTriggerSettings — gleiches Muster, gleiche Struktur.
Beziehung zur Code-Aktion im Workflow. Die integrierte Code-Aktion im Workflow-Builder ist selbst eine Logikfunktion – Twenty erstellt für jeden Code-Schritt eine Logikfunktion und stellt deren Editor inline bereit. workflowActionTriggerSettings ist die Art und Weise, wie Sie diesen einmaligen Inline-Code in eine wiederverwendbare Aktion verwandeln: Definieren Sie die Funktion einmal in Ihrer App, und sie wird in jedem Workflow auswählbar, anstatt in jeden Code-Schritt kopiert zu werden. Siehe die Code-Aktion im Benutzerhandbuch für die Endbenutzeransicht.
src/logic-functions/enrich-company.logic-function.ts
Hauptpunkte:
  • Eine Funktion kann Oberflächen mischen — deklarieren Sie sowohl toolTriggerSettings als auch workflowActionTriggerSettings, um sie im Chat UND im Workflow-Builder bereitzustellen.
  • toolTriggerSettings.inputSchema und workflowActionTriggerSettings.inputSchema sind beide optional. Wenn sie weggelassen werden, leitet der Manifest-Builder sie aus dem Handler-Quellcode ab (JSON Schema für das KI-Tool, das InputSchema von Twenty für die Workflow-Aktion). Geben Sie eines explizit an, wenn Sie eine reichere Typisierung wünschen — zum Beispiel mit FieldMetadataType-fähigen Feldern wie CURRENCY oder RELATION für den Workflow-Builder oder mit description-Feldern, die der KI-Agent lesen kann:
Um Ihre Parameter einmal zu deklarieren und beide Oberflächen zu bedienen, definieren Sie ein einziges JSON Schema (InputJsonSchema) und konvertieren Sie es für die Workflow-Aktion mit jsonSchemaToInputSchema aus twenty-sdk/logic-function. toolTriggerSettings.inputSchema nimmt das JSON Schema direkt, während workflowActionTriggerSettings.inputSchema das InputSchema von Twenty erwartet:
Ein vollständiges Beispiel für eine Workflow-Aktion
workflowActionTriggerSettings akzeptiert vier Felder:Alles zusammen – eine Funktion, die als Workflow-Aktion bereitgestellt wird, mit einer deklarierten Ausgabe, damit spätere Schritte auf taskId verweisen können:
src/logic-functions/enrich-company.logic-function.ts
Sobald die App installiert ist, erscheint Enrich Company im Aktionswähler des Workflow-Builders. Der Builder rendert companyName und domain als Eingabefelder (die jeweils Werte aus vorherigen Schritten beziehen können), und nachgelagerte Schritte können auf die taskId- und enriched-Ausgaben des Schritts verweisen.
Schreiben Sie eine gute description. KI-Agenten verlassen sich auf das description-Feld der Funktion, um zu entscheiden, wann das Tool verwendet werden soll. Seien Sie konkret darin, was das Tool tut und wann es aufgerufen werden soll.
Runtime-Hilfsfunktionen. twenty-sdk/utils exportiert kleine Runtime-Hilfsfunktionen erneut, sodass Handler niemals direkt aus twenty-shared importieren. Zum Beispiel gibt isDefined(value) sowohl für null als auch für undefined den Wert false zurück – verwenden Sie es, um optionale Handler-Eingaben sicher einzugrenzen, die zur Laufzeit als null ankommen können, selbst wenn sie als T | undefined typisiert sind:
Installations-Hooks – Vorinstallations-, Nachinstallations- und Deinstallations-Handler – teilen sich diese Laufzeit, werden aber mit ihren eigenen define-Funktionen deklariert und verwenden keine Trigger-Einstellungen. Siehe Installations-Hooks für definePreInstallLogicFunction, definePostInstallLogicFunction und defineUninstallLogicFunction.

Typisierte API-Clients (twenty-client-sdk)

Das Paket twenty-client-sdk stellt zwei typisierte GraphQL-Clients bereit, um aus Ihren Logikfunktionen und Frontend-Komponenten mit der Twenty-API zu interagieren.
Der CoreApiClient ist der Haupt-Client zum Abfragen und Ändern von Arbeitsbereichsdaten. Er wird während yarn twenty dev oder yarn twenty dev:build aus Ihrem Arbeitsbereichsschema generiert und ist daher vollständig typisiert, passend zu Ihren Objekten und Feldern.
Der Client verwendet eine Selection-Set-Syntax: Übergeben Sie true, um ein Feld einzuschließen, verwenden Sie __args für Argumente, und verschachteln Sie Objekte für Relationen. Sie erhalten vollständige Autovervollständigung und Typprüfung basierend auf Ihrem Arbeitsbereichsschema.
Der CoreApiClient wird zur Entwicklungs-/Build-Zeit generiert. Wenn Sie ihn verwenden, ohne zuvor yarn twenty dev oder yarn twenty dev:build ausgeführt zu haben, wird ein Fehler ausgelöst. Die Generierung erfolgt automatisch — die CLI inspiziert das GraphQL-Schema Ihres Arbeitsbereichs und erzeugt mit @genql/cli einen typisierten Client.

Verwendung von CoreSchema für Typannotationen

CoreSchema stellt TypeScript-Typen bereit, die Ihren Arbeitsbereichsobjekten entsprechen — nützlich zum Typisieren von Komponentenzustand oder Funktionsparametern:
MetadataApiClient ist im SDK bereits vorgefertigt enthalten (keine Generierung erforderlich). Er fragt den Endpunkt /metadata nach Arbeitsbereichskonfiguration, Anwendungen und Datei-Uploads ab.

Dateien hochladen

Der MetadataApiClient enthält eine Methode uploadFile, um Dateien an Felder des Typs Datei anzuhängen:
Hauptpunkte:
  • Sie verwendet den universalIdentifier des Feldes (nicht dessen arbeitsbereichsspezifische ID), sodass Ihr Upload-Code in jedem Arbeitsbereich funktioniert, in dem Ihre App installiert ist.
  • Die zurückgegebene url ist eine signierte URL, mit der Sie auf die hochgeladene Datei zugreifen können.
Wenn Ihr Code auf Twenty ausgeführt wird (Logikfunktionen oder Frontend-Komponenten), injiziert die Plattform Anmeldedaten als Umgebungsvariablen:
  • TWENTY_API_URL — Basis-URL der Twenty-API
  • TWENTY_APP_ACCESS_TOKEN — Kurzlebiger Schlüssel, der auf die Standard-Funktionsrolle Ihrer Anwendung begrenzt ist
Sie müssen diese nicht an die Clients übergeben — sie lesen automatisch aus process.env. Die Berechtigungen des API-Schlüssels werden durch die Rolle bestimmt, die mit defineApplicationRole() deklariert wird (oder über defaultRoleUniversalIdentifier in application-config.ts referenziert wird).