Zum Hauptinhalt springen
Apps befinden sich derzeit in der Alpha-Testphase. Die Funktion ist funktionsfähig, entwickelt sich jedoch noch weiter.

SDK-Ressourcen verwenden (Typen & Konfiguration)

Das twenty-sdk stellt typisierte Bausteine und Hilfsfunktionen bereit, die Sie in Ihrer App verwenden. Im Folgenden finden Sie die wichtigsten Bausteine, mit denen Sie am häufigsten arbeiten.

Hilfsfunktionen

Das SDK stellt Hilfsfunktionen bereit, um die Entitäten Ihrer App zu definieren. Wie in Entitätserkennung beschrieben, müssen Sie export default define<Entity>({...}) verwenden, damit Ihre Entitäten erkannt werden:
FunktionZweck
defineApplicationAnwendungsmetadaten konfigurieren (erforderlich, eine pro App)
defineObjectBenutzerdefinierte Objekte mit Feldern definieren
defineLogicFunctionLogikfunktionen mit Handlern definieren
definePreInstallLogicFunctionEine Pre-Installations-Logikfunktion definieren (eine pro App)
definePostInstallLogicFunctionEine Post-Installations-Logikfunktion definieren (eine pro App)
defineFrontComponentFrontend-Komponenten für benutzerdefinierte UI definieren
defineRoleRollenberechtigungen und Objektzugriff konfigurieren
defineFieldBestehende Objekte mit zusätzlichen Feldern erweitern
defineViewGespeicherte Views für Objekte definieren
defineNavigationMenuItemSeitenleisten-Navigationslinks definieren
defineSkillSkills für KI-Agenten definieren
Diese Funktionen validieren Ihre Konfiguration zur Build-Zeit und bieten IDE-Autovervollständigung sowie Typsicherheit.

Objekte definieren

Benutzerdefinierte Objekte beschreiben sowohl Schema als auch Verhalten für Datensätze in Ihrem Workspace. Verwenden Sie defineObject(), um Objekte mit eingebauter Validierung zu definieren:
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';

enum PostCardStatus {
  DRAFT = 'DRAFT',
  SENT = 'SENT',
  DELIVERED = 'DELIVERED',
  RETURNED = 'RETURNED',
}

export default defineObject({
  universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
  nameSingular: 'postCard',
  namePlural: 'postCards',
  labelSingular: 'Post Card',
  labelPlural: 'Post Cards',
  description: 'A post card object',
  icon: 'IconMail',
  fields: [
    {
      universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
      name: 'content',
      type: FieldType.TEXT,
      label: 'Content',
      description: "Postcard's content",
      icon: 'IconAbc',
    },
    {
      universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
      name: 'recipientName',
      type: FieldType.FULL_NAME,
      label: 'Recipient name',
      icon: 'IconUser',
    },
    {
      universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
      name: 'recipientAddress',
      type: FieldType.ADDRESS,
      label: 'Recipient address',
      icon: 'IconHome',
    },
    {
      universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
      name: 'status',
      type: FieldType.SELECT,
      label: 'Status',
      icon: 'IconSend',
      defaultValue: `'${PostCardStatus.DRAFT}'`,
      options: [
        { value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
        { value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
        { value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
        { value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
      ],
    },
    {
      universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
      name: 'deliveredAt',
      type: FieldType.DATE_TIME,
      label: 'Delivered at',
      icon: 'IconCheck',
      isNullable: true,
      defaultValue: null,
    },
  ],
});
Hauptpunkte:
  • Verwenden Sie defineObject() für eingebaute Validierung und bessere IDE-Unterstützung.
  • Der universalIdentifier muss eindeutig und über Deployments hinweg stabil sein.
  • Jedes Feld benötigt name, type, label und einen eigenen stabilen universalIdentifier.
  • Das Array fields ist optional — Sie können Objekte ohne benutzerdefinierte Felder definieren.
  • Sie können mit yarn twenty entity:add neue Objekte erzeugen; der Assistent führt Sie durch Benennung, Felder und Beziehungen.
Basisfelder werden automatisch erstellt. Wenn Sie ein benutzerdefiniertes Objekt definieren, fügt Twenty automatisch Standardfelder hinzu wie id, name, createdAt, updatedAt, createdBy, updatedBy und deletedAt. Sie müssen diese nicht in Ihrem fields-Array definieren — fügen Sie nur Ihre benutzerdefinierten Felder hinzu. Sie können Standardfelder überschreiben, indem Sie in Ihrem fields-Array ein Feld mit demselben Namen definieren, dies wird jedoch nicht empfohlen.

Anwendungskonfiguration (application-config.ts)

Jede App hat eine einzelne Datei application-config.ts, die Folgendes beschreibt:
  • Was die App ist: Bezeichner, Anzeigename und Beschreibung.
  • Wie ihre Funktionen ausgeführt werden: welche Rolle sie für Berechtigungen verwenden.
  • (Optional) Variablen: Schlüssel–Wert-Paare, die Ihren Funktionen als Umgebungsvariablen zur Verfügung gestellt werden.
  • (Optional) Pre-Installationsfunktion: eine Logikfunktion, die vor der Installation der App ausgeführt wird.
  • (Optional) Post-Installationsfunktion: eine Logikfunktion, die nach der Installation der App ausgeführt wird.
Verwenden Sie defineApplication(), um Ihre Anwendungskonfiguration zu definieren:
// src/application-config.ts
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';

export default defineApplication({
  universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
  displayName: 'My Twenty App',
  description: 'My first Twenty app',
  icon: 'IconWorld',
  applicationVariables: {
    DEFAULT_RECIPIENT_NAME: {
      universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
      description: 'Default recipient name for postcards',
      value: 'Jane Doe',
      isSecret: false,
    },
  },
  defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
});
Notizen:
  • universalIdentifier-Felder sind deterministische IDs, die Sie besitzen; generieren Sie sie einmal und halten Sie sie über Synchronisierungen hinweg stabil.
  • applicationVariables werden zu Umgebungsvariablen für Ihre Funktionen (zum Beispiel ist DEFAULT_RECIPIENT_NAME als process.env.DEFAULT_RECIPIENT_NAME verfügbar).
  • defaultRoleUniversalIdentifier muss mit der Rollendatei übereinstimmen (siehe unten).
  • Pre-Installations- und Post-Installationsfunktionen werden während des Manifest-Builds automatisch erkannt. Siehe Pre-Installationsfunktionen und Post-Installationsfunktionen.

Rollen und Berechtigungen

Anwendungen können Rollen definieren, die Berechtigungen für die Objekte und Aktionen Ihres Workspaces kapseln. Das Feld defaultRoleUniversalIdentifier in application-config.ts legt die Standardrolle fest, die von den Logikfunktionen Ihrer App verwendet wird.
  • Der zur Laufzeit als TWENTY_API_KEY injizierte API-Schlüssel wird von dieser Standard-Funktionsrolle abgeleitet.
  • Der typisierte Client ist auf die dieser Rolle gewährten Berechtigungen beschränkt.
  • Befolgen Sie das Least-Privilege-Prinzip: Erstellen Sie eine dedizierte Rolle nur mit den Berechtigungen, die Ihre Funktionen benötigen, und verweisen Sie dann auf deren universellen Bezeichner.
Standard-Funktionsrolle (*.role.ts)
Wenn Sie eine neue App erzeugen, erstellt die CLI auch eine Standard-Rolldatei. Verwenden Sie defineRole(), um Rollen mit eingebauter Validierung zu definieren:
// src/roles/default-role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';

export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
  'b648f87b-1d26-4961-b974-0908fd991061';

export default defineRole({
  universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
  label: 'Default function role',
  description: 'Default role for function Twenty client',
  canReadAllObjectRecords: false,
  canUpdateAllObjectRecords: false,
  canSoftDeleteAllObjectRecords: false,
  canDestroyAllObjectRecords: false,
  canUpdateAllSettings: false,
  canBeAssignedToAgents: false,
  canBeAssignedToUsers: false,
  canBeAssignedToApiKeys: false,
  objectPermissions: [
    {
      objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
      canReadObjectRecords: true,
      canUpdateObjectRecords: true,
      canSoftDeleteObjectRecords: false,
      canDestroyObjectRecords: false,
    },
  ],
  fieldPermissions: [
    {
      objectUniversalIdentifier: '9f9882af-170c-4879-b013-f9628b77c050',
      fieldUniversalIdentifier: 'b2c37dc0-8ae7-470e-96cd-1476b47dfaff',
      canReadFieldValue: false,
      canUpdateFieldValue: false,
    },
  ],
  permissionFlags: [PermissionFlag.APPLICATIONS],
});
Der universalIdentifier dieser Rolle wird anschließend in application-config.ts als defaultRoleUniversalIdentifier referenziert. Anders ausgedrückt:
  • *.role.ts definiert, was die Standard-Funktionsrolle darf.
  • application-config.ts verweist auf diese Rolle, sodass Ihre Funktionen deren Berechtigungen erben.
Notizen:
  • Beginnen Sie mit der vorab erstellten Rolle und schränken Sie sie schrittweise gemäß dem Least-Privilege-Prinzip ein.
  • Ersetzen Sie objectPermissions und fieldPermissions durch die Objekte/Felder, die Ihre Funktionen benötigen.
  • permissionFlags steuern den Zugriff auf Funktionen auf Plattformebene. Halten Sie sie minimal; fügen Sie nur hinzu, was Sie benötigen.
  • Ein funktionierendes Beispiel finden Sie in der Hello-World-App: packages/twenty-apps/hello-world/src/roles/function-role.ts.

Konfiguration von Logikfunktionen und Einstiegspunkt

Jede Funktionsdatei verwendet defineLogicFunction(), um eine Konfiguration mit einem Handler und optionalen Triggern zu exportieren.
// src/app/createPostCard.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import { CoreApiClient, type Person } from 'twenty-sdk/generated';

const handler = async (params: RoutePayload) => {
  const client = new CoreApiClient();
  const name = 'name' in params.queryStringParameters
    ? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
    : 'Hello world';

  const result = await client.mutation({
    createPostCard: {
      __args: { data: { name } },
      id: true,
      name: true,
    },
  });
  return result;
};

export default defineLogicFunction({
  universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
  name: 'create-new-post-card',
  timeoutSeconds: 2,
  handler,
  triggers: [
    // Public HTTP route trigger '/s/post-card/create'
    {
      universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
      type: 'route',
      path: '/post-card/create',
      httpMethod: 'GET',
      isAuthRequired: false,
    },
    // Cron trigger (CRON pattern)
    // {
    //   universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
    //   type: 'cron',
    //   pattern: '0 0 1 1 *',
    // },
    // Database event trigger
    // {
    //   universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
    //   type: 'databaseEvent',
    //   eventName: 'person.updated',
    //   updatedFields: ['name'],
    // },
  ],
});
Häufige Trigger-Typen:
  • route: Stellt Ihre Funktion unter einem HTTP-Pfad und einer Methode unter dem Endpunkt /s/ bereit:
z. B. path: '/post-card/create', -> Aufruf unter <APP_URL>/s/post-card/create
  • 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
Notizen:
  • Das Array triggers ist optional. Funktionen ohne Trigger können als von anderen Funktionen aufgerufene Utility-Funktionen verwendet werden.
  • Sie können mehrere Trigger-Typen in einer Funktion kombinieren.

Pre-Installationsfunktionen

Eine Pre-Installationsfunktion ist eine Logikfunktion, die automatisch ausgeführt wird, bevor Ihre App in einem Arbeitsbereich installiert wird. Dies ist nützlich für Validierungsaufgaben, Überprüfungen von Voraussetzungen oder die Vorbereitung des Status des Arbeitsbereichs, bevor die Hauptinstallation fortgesetzt wird. Wenn Sie mit create-twenty-app eine neue App erstellen, wird für Sie eine Pre-Installationsfunktion unter src/logic-functions/pre-install.ts erzeugt:
// src/logic-functions/pre-install.ts
import { definePreInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';

const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
  console.log('Pre install logic function executed successfully!', payload.previousVersion);
};

export default definePreInstallLogicFunction({
  universalIdentifier: '<generated-uuid>',
  name: 'pre-install',
  description: 'Runs before installation to prepare the application.',
  timeoutSeconds: 300,
  handler,
});
Sie können die Pre-Installationsfunktion auch jederzeit manuell über die CLI ausführen:
yarn twenty function:execute --preInstall
Hauptpunkte:
  • Pre-Installationsfunktionen verwenden definePreInstallLogicFunction() — eine spezialisierte Variante, die Trigger-Einstellungen (cronTriggerSettings, databaseEventTriggerSettings, httpRouteTriggerSettings, isTool) weglässt.
  • Der Handler erhält ein InstallLogicFunctionPayload mit { previousVersion: string } — die Version der App, die zuvor installiert war (oder eine leere Zeichenkette bei Neuinstallationen).
  • Pro Anwendung ist nur eine Pre-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird.
  • Der universalIdentifier der Funktion wird während des Builds im Anwendungsmanifest automatisch als preInstallLogicFunctionUniversalIdentifier gesetzt — Sie müssen ihn nicht in defineApplication() referenzieren.
  • Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Vorbereitungsvorgänge zu ermöglichen.
  • Pre-Installationsfunktionen benötigen keine Trigger — sie werden von der Plattform vor der Installation oder manuell über function:execute --preInstall aufgerufen.

Post-Installationsfunktionen

Eine Post-Installationsfunktion ist eine Logikfunktion, die automatisch ausgeführt wird, nachdem Ihre App in einem Arbeitsbereich installiert wurde. Dies ist nützlich für einmalige Einrichtungsvorgänge wie das Befüllen mit Standarddaten, das Erstellen erster Datensätze oder das Konfigurieren von Arbeitsbereichseinstellungen. Wenn Sie mit create-twenty-app eine neue App erstellen, wird für Sie eine Post-Installationsfunktion unter src/logic-functions/post-install.ts erzeugt:
// src/logic-functions/post-install.ts
import { definePostInstallLogicFunction, type InstallLogicFunctionPayload } from 'twenty-sdk';

const handler = async (payload: InstallLogicFunctionPayload): Promise<void> => {
  console.log('Post install logic function executed successfully!', payload.previousVersion);
};

export default definePostInstallLogicFunction({
  universalIdentifier: '<generated-uuid>',
  name: 'post-install',
  description: 'Runs after installation to set up the application.',
  timeoutSeconds: 300,
  handler,
});
Sie können die Post-Installationsfunktion auch jederzeit manuell über die CLI ausführen:
yarn twenty function:execute --postInstall
Hauptpunkte:
  • Post-Installationsfunktionen verwenden definePostInstallLogicFunction() — eine spezialisierte Variante, die Trigger-Einstellungen (cronTriggerSettings, databaseEventTriggerSettings, httpRouteTriggerSettings, isTool) weglässt.
  • Der Handler erhält ein InstallLogicFunctionPayload mit { previousVersion: string } — die Version der App, die zuvor installiert war (oder eine leere Zeichenkette bei Neuinstallationen).
  • Pro Anwendung ist nur eine Post-Installationsfunktion zulässig. Der Manifest-Build schlägt fehl, wenn mehr als eine erkannt wird.
  • Der universalIdentifier der Funktion wird während des Builds im Anwendungsmanifest automatisch als postInstallLogicFunctionUniversalIdentifier gesetzt — Sie müssen ihn nicht in defineApplication() referenzieren.
  • Das standardmäßige Timeout ist auf 300 Sekunden (5 Minuten) festgelegt, um längere Einrichtungsvorgänge wie Daten-Seeding zu ermöglichen.
  • Post-Installationsfunktionen benötigen keine Trigger — sie werden von der Plattform während der Installation oder manuell über function:execute --postInstall aufgerufen.

Routen-Trigger-Payload

Breaking Change (v1.16, Januar 2026): Das Format der Routen-Trigger-Payload hat sich geändert. Vor v1.16 wurden Query-Parameter, Pfadparameter und der Body direkt als Payload gesendet. Ab v1.16 sind sie innerhalb eines strukturierten RoutePayload-Objekts verschachtelt.Vor v1.16:
const handler = async (params) => {
  const { param1, param2 } = params; // Direct access
};
Nach v1.16:
const handler = async (event: RoutePayload) => {
  const { param1, param2 } = event.body; // Access via .body
  const { queryParam } = event.queryStringParameters;
  const { id } = event.pathParameters;
};
So migrieren Sie bestehende Funktionen: Aktualisieren Sie Ihren Handler, sodass er nicht mehr direkt aus dem params-Objekt destrukturiert, sondern aus event.body, event.queryStringParameters oder event.pathParameters.
Wenn ein Routen-Trigger Ihre Logikfunktion aufruft, erhält sie ein RoutePayload-Objekt, das dem AWS HTTP API v2-Format entspricht. Importieren Sie den Typ aus twenty-sdk:
import { defineLogicFunction, type RoutePayload } from 'twenty-sdk';

const handler = async (event: RoutePayload) => {
  // Access request data
  const { headers, queryStringParameters, pathParameters, body } = event;

  // HTTP method and path are available in requestContext
  const { method, path } = event.requestContext.http;

  return { message: 'Success' };
};
Der Typ RoutePayload hat die folgende Struktur:
EigenschaftTypBeschreibung
headersRecord<string, string | undefined>HTTP-Header (nur die in forwardedRequestHeaders aufgelisteten)
queryStringParametersRecord<string, string | undefined>Query-String-Parameter (mehrere Werte mit Kommas verbunden)
pathParametersRecord<string, string | undefined>Aus dem Routenmuster extrahierte Pfadparameter (z. B. /users/:id{ id: '123' })
bodyobject | nullGeparster Request-Body (JSON)
isBase64EncodedbooleanGibt an, ob der Body Base64-codiert ist
requestContext.http.methodstringHTTP-Methode (GET, POST, PUT, PATCH, DELETE)
requestContext.http.pathstringRohpfad der Anfrage

Weiterleiten von HTTP-Headern

Standardmäßig werden HTTP-Header von eingehenden Anfragen aus Sicherheitsgründen nicht an Ihre Logikfunktion weitergegeben. Um auf bestimmte Header zuzugreifen, listen Sie diese explizit im Array forwardedRequestHeaders auf:
export default defineLogicFunction({
  universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
  name: 'webhook-handler',
  handler,
  triggers: [
    {
      universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
      type: 'route',
      path: '/webhook',
      httpMethod: 'POST',
      isAuthRequired: false,
      forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
    },
  ],
});
In Ihrem Handler können Sie anschließend auf diese Header zugreifen:
const handler = async (event: RoutePayload) => {
  const signature = event.headers['x-webhook-signature'];
  const contentType = event.headers['content-type'];

  // Validate webhook signature...
  return { received: true };
};
Header-Namen werden in Kleinbuchstaben normalisiert. Greifen Sie mit Schlüsseln in Kleinbuchstaben darauf zu (zum Beispiel event.headers['content-type']).
Sie können neue Funktionen auf zwei Arten erstellen:
  • Generiert: Führen Sie yarn twenty entity:add aus und wählen Sie die Option zum Hinzufügen einer neuen Logikfunktion. Dadurch wird eine Starterdatei mit Handler und Konfiguration erzeugt.
  • Manuell: Erstellen Sie eine neue *.logic-function.ts-Datei und verwenden Sie defineLogicFunction() nach demselben Muster.

Eine Logikfunktion als Tool markieren

Logikfunktionen können als Tools für KI-Agenten und Workflows verfügbar gemacht werden. Wenn eine Funktion als Tool markiert ist, wird sie von den KI-Funktionen von Twenty auffindbar und kann als Schritt in Workflow-Automatisierungen ausgewählt werden. Um eine Logikfunktion als Tool zu markieren, setzen Sie isTool: true und geben Sie ein toolInputSchema an, das die erwarteten Eingabeparameter mithilfe von JSON Schema beschreibt:
// src/logic-functions/enrich-company.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import { CoreApiClient } from 'twenty-sdk/generated';

const handler = async (params: { companyName: string; domain?: string }) => {
  const client = new CoreApiClient();

  const result = await client.mutation({
    createTask: {
      __args: {
        data: {
          title: `Enrich data for ${params.companyName}`,
          body: `Domain: ${params.domain ?? 'unknown'}`,
        },
      },
      id: true,
    },
  });

  return { taskId: result.createTask.id };
};

export default defineLogicFunction({
  universalIdentifier: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
  name: 'enrich-company',
  description: 'Enrich a company record with external data',
  timeoutSeconds: 10,
  handler,
  isTool: true,
  toolInputSchema: {
    type: 'object',
    properties: {
      companyName: {
        type: 'string',
        description: 'The name of the company to enrich',
      },
      domain: {
        type: 'string',
        description: 'The company website domain (optional)',
      },
    },
    required: ['companyName'],
  },
});
Hauptpunkte:
  • isTool (boolean, Standard: false): Wenn auf true gesetzt, wird die Funktion als Tool registriert und steht KI-Agenten und Workflow-Automatisierungen zur Verfügung.
  • toolInputSchema (object, optional): Ein JSON-Schema-Objekt, das die Parameter beschreibt, die Ihre Funktion akzeptiert. KI-Agenten verwenden dieses Schema, um zu verstehen, welche Eingaben das Tool erwartet, und um Aufrufe zu validieren. Falls weggelassen, lautet der Standardwert für das Schema { type: 'object', properties: {} } (keine Parameter).
  • Funktionen mit isTool: false (oder nicht gesetzt) werden nicht als Tools bereitgestellt. Sie können weiterhin direkt ausgeführt oder von anderen Funktionen aufgerufen werden, erscheinen jedoch nicht in der Tool-Erkennung.
  • Tool-Benennung: Wenn als Tool bereitgestellt, wird der Funktionsname automatisch zu logic_function_<name> normalisiert (in Kleinbuchstaben umgewandelt, nicht alphanumerische Zeichen durch Unterstriche ersetzt). Beispielsweise wird enrich-company zu logic_function_enrich_company.
  • Sie können isTool mit Triggern kombinieren — eine Funktion kann gleichzeitig sowohl ein Tool (von KI-Agenten aufrufbar) als auch durch Ereignisse (Cron, Datenbankereignisse, Routen) ausgelöst werden.
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.

Frontend-Komponenten

Frontend-Komponenten ermöglichen es Ihnen, benutzerdefinierte React-Komponenten zu erstellen, die innerhalb der Twenty-UI gerendert werden. Verwenden Sie defineFrontComponent(), um Komponenten mit eingebauter Validierung zu definieren:
// src/front-components/my-widget.tsx
import { defineFrontComponent } from 'twenty-sdk';

const MyWidget = () => {
  return (
    <div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
      <h1>My Custom Widget</h1>
      <p>This is a custom front component for Twenty.</p>
    </div>
  );
};

export default defineFrontComponent({
  universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  name: 'my-widget',
  description: 'A custom widget component',
  component: MyWidget,
});
Hauptpunkte:
  • Frontend-Komponenten sind React-Komponenten, die in isolierten Kontexten innerhalb von Twenty gerendert werden.
  • Das Feld component verweist auf Ihre React-Komponente.
  • Komponenten werden während yarn twenty app:dev automatisch gebaut und synchronisiert.
Sie können neue Frontend-Komponenten auf zwei Arten erstellen:
  • Generiert: Führen Sie yarn twenty entity:add aus und wählen Sie die Option zum Hinzufügen einer neuen Frontend-Komponente.
  • Manuell: Erstellen Sie eine neue .tsx-Datei und verwenden Sie defineFrontComponent() nach demselben Muster.

Fähigkeiten

Skills definieren wiederverwendbare Anweisungen und Fähigkeiten, die KI-Agenten in Ihrem Arbeitsbereich verwenden können. Verwenden Sie defineSkill(), um Skills mit eingebauter Validierung zu definieren:
// src/skills/example-skill.ts
import { defineSkill } from 'twenty-sdk';

export default defineSkill({
  universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  name: 'sales-outreach',
  label: 'Sales Outreach',
  description: 'Guides the AI agent through a structured sales outreach process',
  icon: 'IconBrain',
  content: `You are a sales outreach assistant. When reaching out to a prospect:
1. Research the company and recent news
2. Identify the prospect's role and likely pain points
3. Draft a personalized message referencing specific details
4. Keep the tone professional but conversational`,
});
Hauptpunkte:
  • name ist eine eindeutige Kennung (als Zeichenfolge) für den Skill (kebab-case empfohlen).
  • label ist der menschenlesbare Anzeigename, der in der UI angezeigt wird.
  • content enthält die Skill-Anweisungen — dies ist der Text, den der KI-Agent verwendet.
  • icon (optional) legt das in der UI angezeigte Symbol fest.
  • description (optional) liefert zusätzlichen Kontext zum Zweck des Skills.
Sie können neue Skills auf zwei Arten erstellen:
  • Generiert: Führen Sie yarn twenty entity:add aus und wählen Sie die Option zum Hinzufügen eines neuen Skills.
  • Manuell: Erstellen Sie eine neue Datei und verwenden Sie defineSkill() nach demselben Muster.

Generierte typisierte Clients

Zwei typisierte Clients werden von yarn twenty app:dev automatisch generiert und basierend auf Ihrem Arbeitsbereichs-Schema in node_modules/twenty-sdk/generated gespeichert:
  • CoreApiClient — fragt den /graphql-Endpunkt nach Arbeitsbereichsdaten ab
  • MetadataApiClient — ruft über den Endpunkt /metadata die Arbeitsbereichskonfiguration und Datei-Uploads ab.
import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';

const client = new CoreApiClient();
const { me } = await client.query({ me: { id: true, displayName: true } });

const metadataClient = new MetadataApiClient();
const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
Beide Clients werden von yarn twenty app:dev automatisch neu generiert, sobald sich Ihre Objekte oder Felder ändern.

Laufzeit-Anmeldedaten in Logikfunktionen

Wenn Ihre Funktion auf Twenty läuft, injiziert die Plattform vor der Ausführung Ihres Codes Anmeldedaten als Umgebungsvariablen:
  • TWENTY_API_URL: Basis-URL der Twenty-API, auf die Ihre App abzielt.
  • TWENTY_API_KEY: Kurzlebiger Schlüssel, der auf die Standard-Funktionsrolle Ihrer Anwendung begrenzt ist.
Notizen:
  • Sie müssen dem generierten Client weder URL noch API-Schlüssel übergeben. Er liest TWENTY_API_URL und TWENTY_API_KEY zur Laufzeit aus process.env.
  • Die Berechtigungen des API-Schlüssels werden durch die Rolle bestimmt, auf die in Ihrer application-config.ts über defaultRoleUniversalIdentifier verwiesen wird. Dies ist die Standardrolle, die von den Logikfunktionen Ihrer Anwendung verwendet wird.
  • Anwendungen können Rollen definieren, um das Least-Privilege-Prinzip einzuhalten. Gewähren Sie nur die Berechtigungen, die Ihre Funktionen benötigen, und verweisen Sie dann mit defaultRoleUniversalIdentifier auf den universellen Bezeichner dieser Rolle.

Dateien hochladen

Der generierte MetadataApiClient enthält eine Methode uploadFile, um Dateien an Felder des Typs Datei in Ihren Arbeitsbereichsobjekten anzuhängen. Da Standard-GraphQL-Clients Multipart-Datei-Uploads nicht nativ unterstützen, stellt der Client diese dedizierte Methode bereit, die unter der Haube die GraphQL-Multipart-Anfragespezifikation implementiert.
import { MetadataApiClient } from 'twenty-sdk/generated';
import * as fs from 'fs';

const metadataClient = new MetadataApiClient();

const fileBuffer = fs.readFileSync('./invoice.pdf');

const uploadedFile = await metadataClient.uploadFile(
  fileBuffer,                                         // file contents as a Buffer
  'invoice.pdf',                                      // filename
  'application/pdf',                                  // MIME type (defaults to 'application/octet-stream')
  '58a0a314-d7ea-4865-9850-7fb84e72f30b',            // field universal identifier
);

console.log(uploadedFile);
// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
Die Methodensignatur:
uploadFile(
  fileBuffer: Buffer,
  filename: string,
  contentType: string,
  fieldMetadataUniversalIdentifier: string,
): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
ParameterTypBeschreibung
fileBufferBufferDer Rohinhalt der Datei
filenamestringDer Name der Datei (wird für Speicherung und Anzeige verwendet)
contentTypestringMIME-Typ der Datei (standardmäßig application/octet-stream, wenn weggelassen)
fieldMetadataUniversalIdentifierstringDer universalIdentifier des Dateityp-Felds in Ihrem Objekt
Hauptpunkte:
  • Die Methode uploadFile ist auf dem MetadataApiClient verfügbar, weil die Upload-Mutation vom Endpunkt /metadata aufgelöst wird.
  • Sie verwendet den universalIdentifier des Feldes (nicht dessen arbeitsbereichsspezifische ID), sodass Ihr Upload-Code in jedem Arbeitsbereich funktioniert, in dem Ihre App installiert ist — im Einklang damit, wie Apps Felder überall sonst referenzieren.
  • Die zurückgegebene url ist eine signierte URL, mit der Sie auf die hochgeladene Datei zugreifen können.

Hello-World-Beispiel

Ein minimales End-to-End-Beispiel, das Objekte, Logikfunktionen, Frontend-Komponenten und mehrere Trigger demonstriert, finden Sie hier.