Vai al contenuto principale
Le app sono attualmente in fase alfa. La funzionalità è funzionante ma ancora in evoluzione.

Usa le risorse dell’SDK (tipi e configurazione)

Il pacchetto twenty-sdk fornisce blocchi tipizzati e funzioni helper da usare nella tua app. Di seguito gli elementi principali con cui interagirai più spesso.

Funzioni helper

L’SDK fornisce funzioni helper per definire le entità della tua app. Come descritto in Rilevamento delle entità, devi usare export default define<Entity>({...}) affinché le tue entità vengano rilevate:
FunzioneScopo
defineApplicationConfigura i metadati dell’applicazione (obbligatorio, uno per app)
defineObjectDefinisci oggetti personalizzati con campi
defineLogicFunctionDefinisci funzioni logiche con handler
definePreInstallLogicFunctionDefinisci una funzione logica di pre-installazione (una per app)
definePostInstallLogicFunctionDefinisci una funzione logica di post-installazione (una per app)
defineFrontComponentDefinisci componenti front-end per un’interfaccia utente personalizzata
defineRoleConfigura i permessi dei ruoli e l’accesso agli oggetti
defineFieldEstendi gli oggetti esistenti con campi aggiuntivi
defineViewDefinisci viste salvate per gli oggetti
defineNavigationMenuItemDefinisci i link di navigazione della barra laterale
defineSkillDefinisci le competenze dell’agente IA
Queste funzioni convalidano la configurazione in fase di build e offrono il completamento automatico nell’IDE e la sicurezza dei tipi.

Definizione degli oggetti

Gli oggetti personalizzati descrivono sia lo schema sia il comportamento per i record nel tuo spazio di lavoro. Usa defineObject() per definire oggetti con convalida integrata:
// 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,
    },
  ],
});
Punti chiave:
  • Usa defineObject() per una convalida integrata e un migliore supporto IDE.
  • Il universalIdentifier deve essere univoco e stabile tra i deployment.
  • Ogni campo richiede un name, type, label e il proprio universalIdentifier stabile.
  • L’array fields è facoltativo: puoi definire oggetti senza campi personalizzati.
  • Puoi generare nuovi oggetti con yarn twenty entity:add, che ti guida nella denominazione, nei campi e nelle relazioni.
I campi base vengono creati automaticamente. Quando definisci un oggetto personalizzato, Twenty aggiunge automaticamente i campi standard come id, name, createdAt, updatedAt, createdBy, updatedBy e deletedAt. Non è necessario definirli nel tuo array fields — aggiungi solo i tuoi campi personalizzati. Puoi sovrascrivere i campi predefiniti definendo un campo con lo stesso nome nel tuo array fields, ma non è consigliato.

Configurazione dell’applicazione (application-config.ts)

Ogni app ha un singolo file application-config.ts che descrive:
  • Identità dell’app: identificatori, nome visualizzato e descrizione.
  • Come vengono eseguite le sue funzioni: quale ruolo usano per i permessi.
  • Variabili (opzionali): coppie chiave–valore esposte alle funzioni come variabili d’ambiente.
  • (Opzionale) funzione di pre-installazione: una funzione logica che viene eseguita prima che l’app venga installata.
  • (Opzionale) funzione post-installazione: una funzione logica che viene eseguita dopo l’installazione dell’app.
Usa defineApplication() per definire la configurazione della tua applicazione:
// 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,
});
Note:
  • I campi universalIdentifier sono ID deterministici sotto il tuo controllo; generali una volta e mantienili stabili tra le sincronizzazioni.
  • applicationVariables diventano variabili d’ambiente per le tue funzioni (ad esempio, DEFAULT_RECIPIENT_NAME è disponibile come process.env.DEFAULT_RECIPIENT_NAME).
  • defaultRoleUniversalIdentifier deve corrispondere al file del ruolo (vedi sotto).
  • Le funzioni di pre-installazione e post-installazione vengono rilevate automaticamente durante la build del manifesto. Vedi Funzioni di pre-installazione e Funzioni di post-installazione.

Ruoli e permessi

Le applicazioni possono definire ruoli che incapsulano i permessi sugli oggetti e sulle azioni del tuo spazio di lavoro. Il campo defaultRoleUniversalIdentifier in application-config.ts indica il ruolo predefinito utilizzato dalle funzioni logiche della tua app.
  • La chiave API di runtime iniettata come TWENTY_API_KEY è derivata da questo ruolo funzione predefinito.
  • Il client tipizzato sarà limitato ai permessi concessi a quel ruolo.
  • Segui il principio del privilegio minimo: crea un ruolo dedicato con solo i permessi necessari alle tue funzioni, quindi fai riferimento al suo identificatore universale.
Ruolo funzione predefinito (*.role.ts)
Quando generi una nuova app con lo scaffolder, la CLI crea anche un file di ruolo predefinito. Usa defineRole() per definire ruoli con convalida integrata:
// 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],
});
L’universalIdentifier di questo ruolo viene quindi referenziato in application-config.ts come defaultRoleUniversalIdentifier. In altre parole:
  • *.role.ts definisce ciò che il ruolo funzione predefinito può fare.
  • application-config.ts punta a quel ruolo in modo che le tue funzioni ne ereditino i permessi.
Note:
  • Parti dal ruolo generato dallo scaffolder, quindi restringilo progressivamente seguendo il principio del privilegio minimo.
  • Sostituisci objectPermissions e fieldPermissions con gli oggetti/campi di cui le tue funzioni hanno bisogno.
  • permissionFlags controllano l’accesso alle funzionalità a livello di piattaforma. Mantienili al minimo; aggiungi solo ciò che ti serve.
  • Vedi un esempio funzionante nell’app Hello World: packages/twenty-apps/hello-world/src/roles/function-role.ts.

Configurazione e punto di ingresso della funzione logica

Ogni file di funzione usa defineLogicFunction() per esportare una configurazione con un handler e trigger opzionali.
// 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'],
    // },
  ],
});
Tipi di trigger comuni:
  • route: Espone la funzione su un percorso e metodo HTTP sotto l’endpoint /s/:
es. path: '/post-card/create', -> chiamata su <APP_URL>/s/post-card/create
  • cron: Esegue la tua funzione secondo una pianificazione utilizzando un’espressione CRON.
  • databaseEvent: Viene eseguito sugli eventi del ciclo di vita degli oggetti dello spazio di lavoro. Quando l’operazione dell’evento è updated, è possibile specificare campi specifici da monitorare nell’array updatedFields. Se lasciato non definito o vuoto, qualsiasi aggiornamento attiverà la funzione.
es. person.updated
Note:
  • L’array triggers è facoltativo. Le funzioni senza trigger possono essere utilizzate come funzioni di utilità richiamate da altre funzioni.
  • Puoi combinare più tipi di trigger in un’unica funzione.

Funzioni di pre-installazione

Una funzione di pre-installazione è una funzione logica che viene eseguita automaticamente prima che la tua app venga installata in uno spazio di lavoro. È utile per attività di convalida, controlli dei prerequisiti o per preparare lo stato dello spazio di lavoro prima che proceda l’installazione principale. Quando esegui lo scaffolding di una nuova app con create-twenty-app, viene generata una funzione di pre-installazione in src/logic-functions/pre-install.ts:
// 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,
});
Puoi anche eseguire manualmente la funzione di pre-installazione in qualsiasi momento utilizzando la CLI:
yarn twenty function:execute --preInstall
Punti chiave:
  • Le funzioni di pre-installazione utilizzano definePreInstallLogicFunction() — una variante specializzata che omette le impostazioni dei trigger (cronTriggerSettings, databaseEventTriggerSettings, httpRouteTriggerSettings, isTool).
  • L’handler riceve un InstallLogicFunctionPayload con { previousVersion: string } — la versione dell’app precedentemente installata (oppure una stringa vuota per nuove installazioni).
  • È consentita una sola funzione di pre-installazione per applicazione. La build del manifesto genererà un errore se ne viene rilevata più di una.
  • L’universalIdentifier della funzione viene impostato automaticamente come preInstallLogicFunctionUniversalIdentifier nel manifesto dell’applicazione durante la build — non è necessario farvi riferimento in defineApplication().
  • Il timeout predefinito è impostato a 300 secondi (5 minuti) per consentire attività di preparazione più lunghe.
  • Le funzioni di pre-installazione non necessitano di trigger — vengono invocate dalla piattaforma prima dell’installazione o manualmente tramite function:execute --preInstall.

Funzioni post-installazione

Una funzione post-installazione è una funzione logica che viene eseguita automaticamente dopo che la tua app è stata installata in uno spazio di lavoro. Questo è utile per attività di configurazione una tantum come il popolamento di dati predefiniti, la creazione di record iniziali o la configurazione delle impostazioni dello spazio di lavoro. Quando esegui lo scaffolding di una nuova app con create-twenty-app, viene generata automaticamente una funzione di post-installazione in src/logic-functions/post-install.ts:
// 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,
});
Puoi anche eseguire manualmente la funzione di post-installazione in qualsiasi momento utilizzando la CLI:
yarn twenty function:execute --postInstall
Punti chiave:
  • Le funzioni di post-installazione utilizzano definePostInstallLogicFunction() — una variante specializzata che omette le impostazioni dei trigger (cronTriggerSettings, databaseEventTriggerSettings, httpRouteTriggerSettings, isTool).
  • L’handler riceve un InstallLogicFunctionPayload con { previousVersion: string } — la versione dell’app precedentemente installata (oppure una stringa vuota per nuove installazioni).
  • È consentita una sola funzione di post-installazione per applicazione. La build del manifesto genererà un errore se ne viene rilevata più di una.
  • L’universalIdentifier della funzione viene impostato automaticamente come postInstallLogicFunctionUniversalIdentifier nel manifesto dell’applicazione durante la build — non è necessario farvi riferimento in defineApplication().
  • Il timeout predefinito è impostato a 300 secondi (5 minuti) per consentire attività di configurazione più lunghe, come il popolamento dei dati.
  • Le funzioni di post-installazione non necessitano di trigger — vengono invocate dalla piattaforma durante l’installazione o manualmente tramite function:execute --postInstall.

Payload del trigger di route

Modifica non retrocompatibile (v1.16, gennaio 2026): Il formato del payload del trigger di route è cambiato. Prima della v1.16, i parametri di query, i parametri di percorso e il corpo venivano inviati direttamente come payload. A partire dalla v1.16, sono annidati all’interno di un oggetto RoutePayload strutturato.Prima della v1.16:
const handler = async (params) => {
  const { param1, param2 } = params; // Direct access
};
Dopo la v1.16:
const handler = async (event: RoutePayload) => {
  const { param1, param2 } = event.body; // Access via .body
  const { queryParam } = event.queryStringParameters;
  const { id } = event.pathParameters;
};
Per migrare le funzioni esistenti: Aggiorna l’handler per estrarre i dati da event.body, event.queryStringParameters o event.pathParameters invece che direttamente dall’oggetto params.
Quando un trigger di route invoca la tua funzione logica, questa riceve un oggetto RoutePayload che segue il formato AWS HTTP API v2. Importa il tipo da 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' };
};
Il tipo RoutePayload ha la seguente struttura:
ProprietàTipoDescrizione
headersRecord<string, string | undefined>Intestazioni HTTP (solo quelle elencate in forwardedRequestHeaders)
queryStringParametersRecord<string, string | undefined>Parametri della query string (valori multipli uniti da virgole)
pathParametersRecord<string, string | undefined>Parametri di percorso estratti dal pattern della route (ad es., /users/:id{ id: '123' })
bodyobject | nullCorpo della richiesta analizzato (JSON)
isBase64EncodedbooleanIndica se il corpo è codificato in base64
requestContext.http.methodstringMetodo HTTP (GET, POST, PUT, PATCH, DELETE)
requestContext.http.pathstringPercorso della richiesta non elaborato

Inoltro delle intestazioni HTTP

Per impostazione predefinita, le intestazioni HTTP delle richieste in ingresso non vengono passate alla tua funzione logica per motivi di sicurezza. Per accedere a intestazioni specifiche, elencale esplicitamente nell’array forwardedRequestHeaders:
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'],
    },
  ],
});
Nel tuo handler, puoi quindi accedere a queste intestazioni:
const handler = async (event: RoutePayload) => {
  const signature = event.headers['x-webhook-signature'];
  const contentType = event.headers['content-type'];

  // Validate webhook signature...
  return { received: true };
};
I nomi delle intestazioni vengono normalizzati in minuscolo. Accedile usando chiavi in minuscolo (ad esempio, event.headers['content-type']).
Puoi creare nuove funzioni in due modi:
  • Generata dallo scaffolder: Esegui yarn twenty entity:add e scegli l’opzione per aggiungere una nuova funzione logica. Questo genera un file iniziale con un handler e una configurazione.
  • Manuale: Crea un nuovo file *.logic-function.ts e usa defineLogicFunction(), seguendo lo stesso schema.

Contrassegnare una funzione logica come strumento

Le funzioni logiche possono essere esposte come strumenti per gli agenti di IA e i flussi di lavoro. Quando una funzione è contrassegnata come strumento, diventa individuabile dalle funzionalità di IA di Twenty e può essere selezionata come passaggio nelle automazioni dei flussi di lavoro. Per contrassegnare una funzione logica come strumento, imposta isTool: true e fornisci un toolInputSchema che descriva i parametri di input attesi utilizzando JSON Schema:
// 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'],
  },
});
Punti chiave:
  • isTool (boolean, predefinito: false): Quando impostato su true, la funzione viene registrata come strumento e diventa disponibile per gli agenti IA e le automazioni dei flussi di lavoro.
  • toolInputSchema (object, opzionale): Un oggetto JSON Schema che descrive i parametri accettati dalla funzione. Gli agenti IA utilizzano questo schema per capire quali input si aspetta lo strumento e per convalidare le chiamate. Se omesso, lo schema assume il valore predefinito { type: 'object', properties: {} } (nessun parametro).
  • Le funzioni con isTool: false (o non impostato) non vengono esposte come strumenti. Possono comunque essere eseguite direttamente o chiamate da altre funzioni, ma non compariranno nell’individuazione degli strumenti.
  • Denominazione dello strumento: Quando esposta come strumento, il nome della funzione viene normalizzato automaticamente in logic_function_<name> (in minuscolo, i caratteri non alfanumerici vengono sostituiti da trattini bassi). Ad esempio, enrich-company diventa logic_function_enrich_company.
  • È possibile combinare isTool con i trigger — una funzione può essere sia uno strumento (invocabile dagli agenti IA) sia attivata da eventi (cron, eventi del database, routes) contemporaneamente.
Scrivi una buona description. Gli agenti IA fanno affidamento sul campo description della funzione per decidere quando usare lo strumento. Sii specifico su cosa fa lo strumento e quando dovrebbe essere invocato.

Componenti front-end

I componenti front-end ti consentono di creare componenti React personalizzati che vengono renderizzati all’interno dell’interfaccia di Twenty. Usa defineFrontComponent() per definire componenti con convalida integrata:
// 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,
});
Punti chiave:
  • I componenti front-end sono componenti React che eseguono il rendering in contesti isolati all’interno di Twenty.
  • Il campo component fa riferimento al tuo componente React.
  • I componenti vengono compilati e sincronizzati automaticamente durante yarn twenty app:dev.
Puoi creare nuovi componenti front-end in due modi:
  • Generata dallo scaffolder: Esegui yarn twenty entity:add e scegli l’opzione per aggiungere un nuovo componente front-end.
  • Manuale: Crea un nuovo file .tsx e usa defineFrontComponent(), seguendo lo stesso schema.

Abilità

Le skill definiscono istruzioni e capacità riutilizzabili che gli agenti IA possono utilizzare all’interno del tuo spazio di lavoro. Usa defineSkill() per definire skill con convalida integrata:
// 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`,
});
Punti chiave:
  • name è una stringa identificativa univoca per la skill (kebab-case consigliato).
  • label è il nome di visualizzazione leggibile mostrato nell’UI.
  • content contiene le istruzioni della skill — questo è il testo che l’agente IA utilizza.
  • icon (opzionale) imposta l’icona visualizzata nell’UI.
  • description (opzionale) fornisce contesto aggiuntivo sullo scopo della skill.
Puoi creare nuove skill in due modi:
  • Generata dallo scaffolder: Esegui yarn twenty entity:add e scegli l’opzione per aggiungere una nuova skill.
  • Manuale: Crea un nuovo file e usa defineSkill(), seguendo lo stesso schema.

Client tipizzati generati

Due client tipizzati sono generati automaticamente da yarn twenty app:dev e salvati in node_modules/twenty-sdk/generated in base allo schema del tuo spazio di lavoro:
  • CoreApiClient — interroga l’endpoint /graphql per i dati dello spazio di lavoro
  • MetadataApiClient — interroga l’endpoint /metadata per la configurazione dello spazio di lavoro e il caricamento dei file
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 } });
Entrambi i client vengono rigenerati automaticamente da yarn twenty app:dev ogni volta che i tuoi oggetti o campi cambiano.

Credenziali di runtime nelle funzioni logiche

Quando la tua funzione viene eseguita su Twenty, la piattaforma inietta le credenziali come variabili d’ambiente prima dell’esecuzione del tuo codice:
  • TWENTY_API_URL: URL di base dell’API Twenty a cui punta la tua app.
  • TWENTY_API_KEY: Chiave a breve durata con ambito al ruolo funzione predefinito della tua applicazione.
Note:
  • Non è necessario passare URL o chiave API al client generato. Legge TWENTY_API_URL e TWENTY_API_KEY da process.env in fase di esecuzione.
  • I permessi della chiave API sono determinati dal ruolo referenziato nel tuo application-config.ts tramite defaultRoleUniversalIdentifier. Questo è il ruolo predefinito utilizzato dalle funzioni logiche della tua applicazione.
  • Le applicazioni possono definire ruoli per seguire il principio del privilegio minimo. Concedi solo i permessi necessari alle tue funzioni, quindi punta defaultRoleUniversalIdentifier all’identificatore universale di quel ruolo.

Caricamento dei file

Il MetadataApiClient generato include un metodo uploadFile per allegare file ai campi di tipo file sugli oggetti del tuo spazio di lavoro. Poiché i client GraphQL standard non supportano nativamente il caricamento di file multipart, il client fornisce questo metodo dedicato che implementa la specifica della richiesta GraphQL multipart dietro le quinte.
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://...' }
La firma del metodo:
uploadFile(
  fileBuffer: Buffer,
  filename: string,
  contentType: string,
  fieldMetadataUniversalIdentifier: string,
): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
ParametroTipoDescrizione
fileBufferBufferIl contenuto grezzo del file
filenamestringIl nome del file (utilizzato per l’archiviazione e la visualizzazione)
contentTypestringTipo MIME del file (predefinito su application/octet-stream se omesso)
fieldMetadataUniversalIdentifierstringL’universalIdentifier del campo di tipo file nel tuo oggetto
Punti chiave:
  • Il metodo uploadFile è disponibile su MetadataApiClient perché la mutazione di upload viene risolta dall’endpoint /metadata.
  • Usa l’universalIdentifier del campo (non il suo ID specifico dello spazio di lavoro), quindi il tuo codice di upload funziona in qualsiasi spazio di lavoro in cui la tua app è installata — coerentemente con il modo in cui le app fanno riferimento ai campi altrove.
  • L’url restituito è un URL firmato che puoi usare per accedere al file caricato.

Esempio Hello World

Esplora un esempio minimale end-to-end che dimostra oggetti, funzioni logiche, componenti front-end e trigger multipli qui.