defineLogicFunction
Logikfunktionen und deren Trigger definieren
defineLogicFunction
Logikfunktionen und deren Trigger definieren
Jede Funktionsdatei verwendet Verfügbare Trigger-Typen:Der Typ
Greifen Sie in Ihrem Handler wie folgt auf die weitergeleiteten Header zu:Aus Sicherheitsgründen sind Antwort-Header auf eine Allowlist beschränkt. Jeder Header, der nicht auf der Liste steht (z. B. Die Funktion ist erreichbar unter:Beide Bezeichner sind die
Der aufgelöste Wert muss eine gültige Workspace-UUID sein und deine App muss in diesem Workspace installiert sein, andernfalls wird die Anfrage abgelehnt, bevor die Funktion ausgeführt wird.Die meisten Provider signieren mit HMAC-SHA256; die Teile, die sich unterscheiden, sind der Header-Name, die Digest-Codierung und der signierte Payload-String. Einige Beispiele:
Die Nutzlast umfasst:
Bei Soft Deletes folgt 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:Hauptpunkte:Um Ihre Parameter einmal zu deklarieren und beide Oberflächen zu bedienen, definieren Sie ein einziges JSON Schema (
defineLogicFunction(), um eine Konfiguration mit einem Handler und optionalen Triggern zu exportieren.src/logic-functions/createPostCard.logic-function.ts
- httpRoute: Stellt Ihre Funktion unter einem HTTP-Pfad und einer Methode unter dem Endpunkt
/s/bereit:
z. B.path: '/post-card/create'ist unterhttps://your-twenty-server.com/s/post-card/createaufrufbar
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
updatedist, können bestimmte zu überwachende Felder im ArrayupdatedFieldsangegeben werden. Wenn das Array undefiniert oder leer ist, löst jede Aktualisierung die Funktion aus.
z. B.person.updated,*.created,company.*
- serverWebhook: Empfängt eingehende Webhooks von einem Drittanbieterdienst (Stripe, GitHub, Svix, …) an einem einzelnen, registrierungsbezogenen Endpunkt und ermittelt den Ziel-Arbeitsbereich aus der Nutzlast. Siehe Server-Webhook-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 einRoutePayload-Objekt, das dem AWS-HTTP-API-v2-Format folgt.
Importieren Sie den Typ RoutePayload aus twenty-sdk/logic-function:RoutePayload hat die folgende Struktur:| Eigenschaft | Typ | Beschreibung | Beispiel |
|---|---|---|---|
headers | Record\<string, string | undefined> | HTTP-Header (nur die in forwardedRequestHeaders aufgelisteten) | siehe Abschnitt unten |
queryStringParameters | Record\<string, string | undefined> | Query-String-Parameter (mehrere Werte mit Kommas verbunden) | /users?ids=1&ids=2&ids=3&name=Alice -> { ids: '1,2,3', name: 'Alice' } |
pathParameters | Record\<string, string | undefined> | Aus dem Routenmuster extrahierte Pfadparameter | /users/:id, /users/123 -> { id: '123' } |
body | object | null | Geparster Request-Body (JSON) | { id: 1 } -> { id: 1 } |
rawBody | string | undefined | Ursprünglicher UTF-8-Request-Body vor dem JSON-Parsing. Nützlich zur Verifizierung von Webhook-Signaturen im HMAC-Stil (z. B. GitHubs X-Hub-Signature-256, Stripe). undefined, wenn die Laufzeitumgebung es nicht beibehalten hat. | |
isBase64Encoded | boolean | Gibt an, ob der Body Base64-codiert ist | |
requestContext.http.method | Zeichenkette | HTTP-Methode (GET, POST, PUT, PATCH, DELETE) | |
requestContext.http.path | string | Rohpfad der Anfrage |
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 ArrayforwardedRequestHeaders auf: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 als200-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: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-typecontent-languagecontent-dispositioncache-controlretry-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-Webhook-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 Webhook-URL. Verwende für diesen Fall serverWebhookTriggerSettings: Die Funktion ist über einen registrierungsbezogenen Endpunkt erreichbar und der Workspace wird aus der Nutzlast ermittelt.src/logic-functions/handle-provider-webhook.logic-function.ts
universalIdentifier aus deinem Manifest — der der Anwendungsregistrierung und dieser Logikfunktion. Registriere diese URL beim Provider.Workspace-Auflösung. Da ein Endpunkt jeden Workspace bedient, muss deine Integration die Ziel-workspaceId irgendwo in der Zustellung platzieren, und workspaceIdResolver.{ source, path } teilt der Plattform mit, wo sie diese auslesen soll:| Feld | Werte | Notizen |
|---|---|---|
quelle | body | query | header | body liest das geparste JSON. query ist am universellsten — du kontrollierst in der Regel die Callback-URL, die du registrierst, also hänge ?twentyWorkspaceId=… an. |
pfad | Dot-Pfad, z. B. metadata.twentyWorkspaceId | Beschränkt auf alphanumerische / _- / --Segmente; Prototype-Schlüssel werden abgelehnt. |
| Anbieter | Weiterzuleitende Header | Signierte Zeichenkette | Digest |
|---|---|---|---|
| Svix (Recall, Resend, Clerk) | webhook-id, webhook-timestamp, webhook-signature | {id}.{timestamp}.{rawBody} | base64 (Geheimnis ist base64 nach Entfernen von whsec_) |
| Stripe | stripe-signature | {timestamp}.{rawBody} | hex |
| GitHub | x-hub-signature-256 | {rawBody} | hex (mit Präfix sha256=) |
| Shopify | x-shopify-hmac-sha256 | {rawBody} | base64 |
| Slack | x-slack-signature, x-slack-request-timestamp | v0:{timestamp}:{rawBody} | hex (mit Präfix v0=) |
Die Funktion läuft synchron und dein Rückgabewert wird zur HTTP-Antwort, sodass Provider deinen Statuscode sehen und bei Nicht-2xx erneut versuchen können. Halte Handler schnell — einige Provider (z. B. Slack) laufen nach wenigen Sekunden in ein Timeout. Da die Funktion ausgeführt wird, bevor die Signatur geprüft wird, solltest du diesen Endpunkt an deinem Edge mit Ratenbegrenzung schützen.
Datenbank-Event-Trigger-Payload
Wenn ein Datenbank-Event-Trigger Ihre Logic Function aufruft, erhält sie eineDatabaseEventPayload pro geändertem Datensatz. Die Payload kombiniert Metadaten über den Quell-Workspace und das Objekt mit dem Ereignis auf Datensatzebene.| Eigenschaft | Beschreibung |
|---|---|
name | Ereignisname, z. B. person.updated. |
workspaceId | Arbeitsbereich, in dem das Ereignis stattgefunden hat. |
objectMetadata | Metadaten für das Objekt, das geändert wurde. |
recordId | ID des geänderten Datensatzes. |
userId, userWorkspaceId, workspaceMemberId | Akteurfelder, wenn das Ereignis von einem Benutzer des Arbeitsbereichs ausgelöst wurde. |
properties | Datensatzdaten für das Ereignis mit before, after, diff und updatedFields, abhängig von der Operation. |
| Ereignis | Datensatzdaten |
|---|---|
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 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.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 umfangreicheInputSchemavon Twenty, sodass der Builder geeignete Feldeditoren, Variablenauswahlen und Beschriftungen rendern kann.
cronTriggerSettings, databaseEventTriggerSettings und httpRouteTriggerSettings — gleiches Muster, gleiche Struktur.src/logic-functions/enrich-company.logic-function.ts
- Eine Funktion kann Oberflächen mischen — deklarieren Sie sowohl
toolTriggerSettingsals auchworkflowActionTriggerSettings, um sie im Chat UND im Workflow-Builder bereitzustellen. toolTriggerSettings.inputSchemaundworkflowActionTriggerSettings.inputSchemasind beide optional. Wenn sie weggelassen werden, leitet der Manifest-Builder sie aus dem Handler-Quellcode ab (JSON Schema für das KI-Tool, dasInputSchemavon Twenty für die Workflow-Aktion). Geben Sie eines explizit an, wenn Sie eine reichere Typisierung wünschen — zum Beispiel mitFieldMetadataType-fähigen Feldern wieCURRENCYoderRELATIONfür den Workflow-Builder oder mitdescription-Feldern, die der KI-Agent lesen kann:
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: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- und Nachinstallations-Handler – teilen sich diese Laufzeit, werden aber mit ihren eigenen define-Funktionen deklariert und verwenden keine Trigger-Einstellungen. Siehe Installations-Hooks für
definePreInstallLogicFunction und definePostInstallLogicFunction.Typisierte API-Clients (twenty-client-sdk)
Das Pakettwenty-client-sdk stellt zwei typisierte GraphQL-Clients bereit, um aus Ihren Logikfunktionen und Frontend-Komponenten mit der Twenty-API zu interagieren.
| Client | Importieren | Endpunkt | Generiert? |
|---|---|---|---|
CoreApiClient | twenty-client-sdk/core | /graphql — Arbeitsbereichsdaten (Datensätze, Objekte) | Ja, zur Entwicklungs-/Build-Zeit |
MetadataApiClient | twenty-client-sdk/metadata | /metadata — Arbeitsbereichskonfiguration, Datei-Uploads | Nein, wird vorgefertigt ausgeliefert |
CoreApiClient
Arbeitsbereichsdaten (Datensätze, Objekte) abfragen und ändern
CoreApiClient
Arbeitsbereichsdaten (Datensätze, Objekte) abfragen und ändern
Der Der Client verwendet eine Selection-Set-Syntax: Übergeben Sie
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.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
Konfiguration des Arbeitsbereichs, Anwendungen und Dateiuploads
MetadataApiClient
Konfiguration des Arbeitsbereichs, Anwendungen und Dateiuploads
MetadataApiClient ist im SDK bereits vorgefertigt enthalten (keine Generierung erforderlich). Er fragt den Endpunkt /metadata nach Arbeitsbereichskonfiguration, Anwendungen und Datei-Uploads ab.Dateien hochladen
DerMetadataApiClient enthält eine Methode uploadFile, um Dateien an Felder des Typs Datei anzuhängen:| Parameter | Typ | Beschreibung |
|---|---|---|
fileBuffer | Buffer | Der Rohinhalt der Datei |
filename | string | Der Name der Datei (wird für Speicherung und Anzeige verwendet) |
contentType | string | MIME-Typ (standardmäßig application/octet-stream, wenn weggelassen) |
fieldMetadataUniversalIdentifier | string | Der universalIdentifier des Dateityp-Felds in Ihrem Objekt |
- Sie verwendet den
universalIdentifierdes Feldes (nicht dessen arbeitsbereichsspezifische ID), sodass Ihr Upload-Code in jedem Arbeitsbereich funktioniert, in dem Ihre App installiert ist. - Die zurückgegebene
urlist 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-APITWENTY_APP_ACCESS_TOKEN— Kurzlebiger Schlüssel, der auf die Standard-Funktionsrolle Ihrer Anwendung begrenzt ist
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).