Salt la conținutul principal
Aplicațiile sunt în prezent în testare alfa. Caracteristica funcționează, dar este încă în dezvoltare.

Ce sunt aplicațiile?

Aplicațiile vă permit să construiți și să gestionați personalizările Twenty sub formă de cod. În loc să configurați totul prin interfața de utilizator (UI), vă definiți modelul de date și funcțiile de logică în cod — făcând mai rapidă construirea, mentenanța și implementarea în mai multe spații de lucru. Ce puteți face astăzi:
  • Definiți obiecte și câmpuri personalizate sub formă de cod (model de date gestionat)
  • Creați funcții de logică cu declanșatoare personalizate
  • Definiți abilități pentru agenți de IA
  • Implementați aceeași aplicație în mai multe spații de lucru

Cerințe

Începeți

Creați o aplicație nouă folosind generatorul oficial, apoi autentificați-vă și începeți să dezvoltați:
# Creează scheletul unei aplicații noi (include toate exemplele în mod implicit)
npx create-twenty-app@latest my-twenty-app
cd my-twenty-app

# Dacă nu folosești yarn@4
corepack enable
yarn install

# Autentifică-te folosind cheia ta API (ți se va solicita)
yarn twenty auth:login

# Pornește modul de dezvoltare: sincronizează automat modificările locale cu spațiul tău de lucru
yarn twenty app:dev
Generatorul de schelet acceptă trei moduri pentru a controla ce fișiere de exemplu sunt incluse:
# Implicit (exhaustiv): toate exemplele (obiect, câmp, funcție logică, componentă de interfață, vizualizare, element de meniu de navigare, abilitate)
npx create-twenty-app@latest my-app

# Minimal: doar fișierele de bază (application-config.ts și default-role.ts)
npx create-twenty-app@latest my-app --minimal

# Interactiv: selectezi ce exemple să incluzi
npx create-twenty-app@latest my-app --interactive
De aici puteți:
# Adaugă o entitate nouă în aplicația ta (ghidat)
yarn twenty entity:add

# Urmărește jurnalele funcțiilor aplicației tale
yarn twenty function:logs

# Execută o funcție după nume
yarn twenty function:execute -n my-function -p '{"name": "test"}'

# Execută funcția post-instalare
yarn twenty function:execute --postInstall

# Dezinstalează aplicația din spațiul de lucru curent
yarn twenty app:uninstall

# Afișează ajutorul pentru comenzi
yarn twenty help
Consultați și: paginile de referință CLI pentru create-twenty-app și twenty-sdk CLI.

Structura proiectului (generată)

Când rulați npx create-twenty-app@latest my-twenty-app, generatorul:
  • Copiază o aplicație de bază minimală în my-twenty-app/
  • Adaugă o dependență locală twenty-sdk și configurația Yarn 4
  • Creează fișiere de configurare și scripturi conectate la CLI-ul twenty
  • Generează fișierele de bază (configurația aplicației, rolul implicit al funcțiilor, funcția post-instalare) plus fișiere de exemplu în funcție de modul de generare a scheletului
O aplicație proaspăt generată cu modul implicit --exhaustive arată astfel:
my-twenty-app/
  package.json
  yarn.lock
  .gitignore
  .nvmrc
  .yarnrc.yml
  .yarn/
    install-state.gz
  eslint.config.mjs
  tsconfig.json
  README.md
  public/                           # Director pentru resurse publice (imagini, fonturi etc.)
  src/
  ├── application-config.ts           # Obligatoriu - configurația principală a aplicației
  ├── roles/
  │   └── default-role.ts               # Rol implicit pentru funcțiile logice
  ├── objects/
  │   └── example-object.ts             # Exemplu de definiție a unui obiect personalizat
  ├── fields/
  │   └── example-field.ts              # Exemplu de definiție de câmp independent
  ├── logic-functions/
  │   ├── hello-world.ts                # Exemplu de funcție logică
  │   └── post-install.ts               # Funcție logică post-instalare
  ├── front-components/
  │   └── hello-world.tsx               # Exemplu de componentă de interfață
  ├── views/
  │   └── example-view.ts                # Exemplu de definiție a unei vizualizări salvate
  ├── navigation-menu-items/
  │   └── example-navigation-menu-item.ts # Exemplu de link de navigare în bara laterală
  └── skills/
      └── example-skill.ts                # Exemplu de definiție a unei abilități a agentului AI
Cu --minimal, sunt create doar fișierele de bază (application-config.ts, roles/default-role.ts și logic-functions/post-install.ts). Cu --interactive, alegi ce fișiere de exemplu să incluzi. Pe scurt:
  • package.json: Declară numele aplicației, versiunea, motoarele (Node 24+, Yarn 4) și adaugă twenty-sdk plus un script twenty care deleagă către CLI-ul local twenty. Rulează yarn twenty help pentru a lista toate comenzile disponibile.
  • .gitignore: Ignoră artefacte comune precum node_modules, .yarn, generated/ (client tipizat), dist/, build/, foldere de coverage, fișiere jurnal și fișiere .env*.
  • yarn.lock, .yarnrc.yml, .yarn/: Blochează și configurează lanțul de instrumente Yarn 4 folosit de proiect.
  • .nvmrc: Fixează versiunea Node.js așteptată de proiect.
  • eslint.config.mjs și tsconfig.json: Oferă linting și configurație TypeScript pentru fișierele TypeScript ale aplicației.
  • README.md: Un README scurt în rădăcina aplicației, cu instrucțiuni de bază.
  • public/: Un folder pentru stocarea resurselor publice (imagini, fonturi, fișiere statice) care vor fi servite împreună cu aplicația ta. Fișierele plasate aici sunt încărcate în timpul sincronizării și sunt accesibile la rulare.
  • src/: Locul principal unde vă definiți aplicația sub formă de cod

Detectarea entităților

SDK-ul detectează entitățile analizând fișierele TypeScript pentru apeluri export default define<Entity>({...}). Fiecare tip de entitate are o funcție ajutătoare corespunzătoare, exportată din twenty-sdk:
Funcție ajutătoareTipul entității
defineObject()Definiții de obiecte personalizate
defineLogicFunction()Definiții de funcții de logică
defineFrontComponent()Definiții ale componentelor de interfață
defineRole()Definiții de rol
defineField()Extensii de câmp pentru obiectele existente
defineView()Definiții pentru vizualizări salvate
defineNavigationMenuItem()Definiții pentru elemente de meniu de navigare
defineSkill()Definiții ale abilităților agentului AI
Denumirea fișierelor este flexibilă. Detectarea entităților se bazează pe AST — SDK-ul scanează fișierele sursă pentru tiparul export default define<Entity>({...}). Puteți organiza fișierele și folderele cum doriți. Gruparea după tipul de entitate (de exemplu, logic-functions/, roles/) este doar o convenție pentru organizarea codului, nu o cerință.
Exemplu de entitate detectată:
// This file can be named anything and placed anywhere in src/
import { defineObject, FieldType } from 'twenty-sdk';

export default defineObject({
  universalIdentifier: '...',
  nameSingular: 'postCard',
  // ... rest of config
});
Comenzile ulterioare vor adăuga mai multe fișiere și foldere:
  • yarn twenty app:dev va genera automat un client API tipizat în node_modules/twenty-sdk/generated (client Twenty tipizat + tipuri ale spațiului de lucru).
  • yarn twenty entity:add va adăuga fișiere de definire a entităților în src/ pentru obiectele, funcțiile, componentele front-end, rolurile, abilitățile și altele.

Autentificare

Prima dată când rulați yarn twenty auth:login, vi se vor solicita: Acreditările dvs. sunt stocate per utilizator în ~/.twenty/config.json. Puteți menține mai multe profiluri și comuta între ele.

Gestionarea spațiilor de lucru

# Login interactively (recommended)
yarn twenty auth:login

# Login to a specific workspace profile
yarn twenty auth:login --workspace my-custom-workspace

# List all configured workspaces
yarn twenty auth:list

# Switch the default workspace (interactive)
yarn twenty auth:switch

# Switch to a specific workspace
yarn twenty auth:switch production

# Check current authentication status
yarn twenty auth:status
După ce ați schimbat spațiul de lucru cu yarn twenty auth:switch, toate comenzile ulterioare vor folosi implicit acel spațiu de lucru. Îl puteți totuși suprascrie temporar cu --workspace <name>.

Utilizați resursele SDK (tipuri și configurare)

Biblioteca twenty-sdk oferă blocuri de bază tipizate și funcții ajutătoare pe care le utilizați în aplicația dvs. Mai jos sunt elementele cheie cu care veți interacționa cel mai des.

Funcții ajutătoare

SDK-ul oferă funcții ajutătoare pentru definirea entităților aplicației. După cum este descris în Detectarea entităților, trebuie să folosiți export default define<Entity>({...}) pentru ca entitățile să fie detectate:
FuncțieScop
defineApplication()Configurați metadatele aplicației (obligatoriu, una per aplicație)
defineObject()Definiți obiecte personalizate cu câmpuri
defineLogicFunction()Definiți funcții de logică cu handleri
defineFrontComponent()Definiți componente Front pentru interfața de utilizator personalizată
defineRole()Configurați permisiunile rolurilor și accesul la obiecte
defineField()Extindeți obiectele existente cu câmpuri suplimentare
defineView()Definește vizualizări salvate pentru obiecte
defineNavigationMenuItem()Definește linkuri de navigare în bara laterală
defineSkill()Definiți abilități pentru agentul AI
Aceste funcții validează configurația în timpul build-ului și oferă completare automată în IDE și siguranța tipurilor.

Definirea obiectelor

Obiectele personalizate descriu atât schema, cât și comportamentul înregistrărilor din spațiul dvs. de lucru. Utilizați defineObject() pentru a defini obiecte cu validare încorporată:
// 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,
    },
  ],
});
Puncte cheie:
  • Folosiți defineObject() pentru validare încorporată și suport mai bun în IDE.
  • universalIdentifier trebuie să fie unic și stabil între implementări.
  • Fiecare câmp necesită un name, un type, un label și propriul universalIdentifier stabil.
  • Matricea fields este opțională — puteți defini obiecte fără câmpuri personalizate.
  • Puteți genera obiecte noi folosind yarn twenty entity:add, care vă ghidează prin denumire, câmpuri și relații.
Câmpurile de bază sunt create automat. Când definiți un obiect personalizat, Twenty adaugă automat câmpuri standard precum id, name, createdAt, updatedAt, createdBy, updatedBy și deletedAt. Nu trebuie să le definiți în tabloul fields — adăugați doar câmpurile personalizate proprii. Puteți suprascrie câmpurile implicite definind un câmp cu același nume în tabloul fields, dar acest lucru nu este recomandat.

Configurația aplicației (application-config.ts)

Fiecare aplicație are un singur fișier application-config.ts care descrie:
  • Cine este aplicația: identificatori, nume de afișare și descriere.
  • Cum rulează funcțiile: ce rol folosesc pentru permisiuni.
  • (Opțional) variabile: perechi cheie–valoare expuse funcțiilor ca variabile de mediu.
  • (Opțional) funcție post-instalare: o funcție logică care rulează după instalarea aplicației.
Folosiți defineApplication() pentru a defini configurația aplicației:
// src/application-config.ts
import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';

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,
  postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
});
Notițe:
  • Câmpurile universalIdentifier sunt ID-uri deterministe pe care le dețineți; generați-le o singură dată și păstrați-le stabile între sincronizări.
  • applicationVariables devin variabile de mediu pentru funcțiile dvs. (de exemplu, DEFAULT_RECIPIENT_NAME este disponibil ca process.env.DEFAULT_RECIPIENT_NAME).
  • defaultRoleUniversalIdentifier trebuie să corespundă fișierului de rol (vedeți mai jos).
  • postInstallLogicFunctionUniversalIdentifier (opțional) indică o funcție logică care rulează automat după instalarea aplicației. Vezi Funcții post-instalare.

Roluri și permisiuni

Aplicațiile pot defini roluri care încapsulează permisiuni asupra obiectelor și acțiunilor din spațiul dvs. de lucru. Câmpul defaultRoleUniversalIdentifier din application-config.ts desemnează rolul implicit utilizat de funcțiile de logică ale aplicației.
  • Cheia API de runtime injectată ca TWENTY_API_KEY este derivată din acest rol implicit pentru funcții.
  • Clientul tipizat va fi restricționat la permisiunile acordate acelui rol.
  • Respectați principiul celui mai mic privilegiu: creați un rol dedicat doar cu permisiunile de care au nevoie funcțiile, apoi referiți identificatorul său universal.
Rol implicit pentru funcții (*.role.ts)
Când generați o aplicație nouă, CLI creează și un fișier de rol implicit. Folosiți defineRole() pentru a defini roluri cu validare încorporată:
// 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],
});
universalIdentifier al acestui rol este apoi referențiat în application-config.ts ca defaultRoleUniversalIdentifier. Cu alte cuvinte:
  • *.role.ts definește ce poate face rolul implicit pentru funcții.
  • application-config.ts indică acel rol, astfel încât funcțiile moștenesc permisiunile lui.
Notițe:
  • Porniți de la rolul generat, apoi restrângeți-l progresiv urmând principiul celui mai mic privilegiu.
  • Înlocuiți objectPermissions și fieldPermissions cu obiectele/câmpurile de care au nevoie funcțiile.
  • permissionFlags controlează accesul la capabilități la nivelul platformei. Mențineți-le la minimum; adăugați doar ceea ce aveți nevoie.
  • Vedeți un exemplu funcțional în aplicația Hello World: packages/twenty-apps/hello-world/src/roles/function-role.ts.

Configurația funcției de logică și punctul de intrare

Fiecare fișier de funcție folosește defineLogicFunction() pentru a exporta o configurație cu un handler și declanșatoare opționale.
// src/app/createPostCard.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import Twenty, { type Person } from '~/generated';

const handler = async (params: RoutePayload) => {
  const client = new Twenty(); // generated typed client
  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'],
    // },
  ],
});
Tipuri comune de declanșatoare:
  • route: Expune funcția pe o rută și metodă HTTP sub endpoint-ul /s/:
de ex. path: '/post-card/create', -> apel pe <APP_URL>/s/post-card/create
  • cron: Rulează funcția pe un program folosind o expresie CRON.
  • databaseEvent: Rulează la evenimentele ciclului de viață ale obiectelor din spațiul de lucru. Când operațiunea evenimentului este updated, câmpurile specifice de urmărit pot fi specificate în array-ul updatedFields. Dacă este lăsat nedefinit sau gol, orice actualizare va declanșa funcția.
de ex. person.updated
Notițe:
  • Matricea triggers este opțională. Funcțiile fără declanșatoare pot fi folosite ca funcții utilitare apelate de alte funcții.
  • Puteți combina mai multe tipuri de declanșatoare într-o singură funcție.

Funcții post-instalare

O funcție post-instalare este o funcție logică care rulează automat după instalarea aplicației într-un spațiu de lucru. Aceasta este utilă pentru sarcini de configurare unice, cum ar fi popularea cu date implicite, crearea înregistrărilor inițiale sau configurarea setărilor spațiului de lucru. Când creezi scheletul unei aplicații noi cu create-twenty-app, este generată o funcție post-instalare la src/logic-functions/post-install.ts:
// src/logic-functions/post-install.ts
import { defineLogicFunction } from 'twenty-sdk';

export const POST_INSTALL_UNIVERSAL_IDENTIFIER = '<generated-uuid>';

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

export default defineLogicFunction({
  universalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
  name: 'post-install',
  description: 'Runs after installation to set up the application.',
  timeoutSeconds: 300,
  handler,
});
Funcția este integrată în aplicația ta prin referirea la identificatorul său universal în application-config.ts:
import { POST_INSTALL_UNIVERSAL_IDENTIFIER } from 'src/logic-functions/post-install';

export default defineApplication({
  // ...
  postInstallLogicFunctionUniversalIdentifier: POST_INSTALL_UNIVERSAL_IDENTIFIER,
});
Poți, de asemenea, să execuți manual funcția post-instalare oricând folosind CLI:
yarn twenty function:execute --postInstall
Puncte cheie:
  • Funcțiile post-instalare sunt funcții logice standard — folosesc defineLogicFunction() la fel ca orice altă funcție.
  • Câmpul postInstallLogicFunctionUniversalIdentifier din defineApplication() este opțional. Dacă este omis, nu rulează nicio funcție după instalare.
  • Timpul de expirare implicit este setat la 300 de secunde (5 minute) pentru a permite sarcini de configurare mai lungi, cum ar fi popularea datelor.
  • Funcțiile post-instalare nu au nevoie de declanșatoare — sunt invocate de platformă în timpul instalării sau manual prin function:execute --postInstall.

Payload-ul declanșatorului de rută

Modificare incompatibilă (v1.16, ianuarie 2026): Formatul payload-ului declanșatorului de rută s-a schimbat. Înainte de v1.16, parametrii de interogare (query), parametrii de cale și corpul erau trimiși direct ca payload. Începând cu v1.16, acestea sunt incluse într-un obiect structurat RoutePayload.Înainte de v1.16:
const handler = async (params) => {
  const { param1, param2 } = params; // Direct access
};
După v1.16:
const handler = async (event: RoutePayload) => {
  const { param1, param2 } = event.body; // Access via .body
  const { queryParam } = event.queryStringParameters;
  const { id } = event.pathParameters;
};
Pentru a migra funcțiile existente: Actualizează handler-ul pentru a extrage câmpurile din event.body, event.queryStringParameters sau event.pathParameters în loc să le iei direct din obiectul params.
Când un declanșator de rută apelează funcția dvs. de logică, aceasta primește un obiect RoutePayload care urmează formatul AWS HTTP API v2. Importă tipul din 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' };
};
Tipul RoutePayload are următoarea structură:
ProprietateTipDescriere
headersRecord<string, string | undefined>Anteturi HTTP (doar cele listate în forwardedRequestHeaders)
queryStringParametersRecord<string, string | undefined>Parametri query string (valorile multiple unite cu virgule)
pathParametersRecord<string, string | undefined>Parametri de cale extrași din modelul rutei (de ex., /users/:id{ id: '123' })
corpobject | nullCorpul cererii analizat (JSON)
isBase64EncodedbooleanIndică dacă corpul este codificat în base64
requestContext.http.methodstringMetoda HTTP (GET, POST, PUT, PATCH, DELETE)
requestContext.http.pathstringCalea brută a cererii

Transmiterea anteturilor HTTP

În mod implicit, anteturile HTTP din cererile de intrare nu sunt transmise funcției dvs. de logică din motive de securitate. Pentru a accesa anumite anteturi, listează-le explicit în array-ul 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'],
    },
  ],
});
În handler, poți apoi accesa aceste anteturi:
const handler = async (event: RoutePayload) => {
  const signature = event.headers['x-webhook-signature'];
  const contentType = event.headers['content-type'];

  // Validate webhook signature...
  return { received: true };
};
Numele anteturilor sunt normalizate la litere mici. Accesează-le folosind chei cu litere mici (de exemplu, event.headers['content-type']).
Puteți crea funcții noi în două moduri:
  • Generat: Rulați yarn twenty entity:add și alegeți opțiunea de a adăuga o funcție logică nouă. Aceasta generează un fișier inițial cu un handler și o configurație.
  • Manual: Creați un fișier nou *.logic-function.ts și folosiți defineLogicFunction(), urmând același model.

Marcarea unei funcții logice drept instrument

Funcțiile logice pot fi expuse ca instrumente pentru agenți de IA și fluxuri de lucru. Când o funcție este marcată ca instrument, poate fi descoperită de funcționalitățile de IA ale Twenty și poate fi selectată ca pas în automatizări ale fluxurilor de lucru. Pentru a marca o funcție logică drept instrument, setați isTool: true și furnizați un toolInputSchema care descrie parametrii de intrare așteptați folosind JSON Schema:
// src/logic-functions/enrich-company.logic-function.ts
import { defineLogicFunction } from 'twenty-sdk';
import Twenty from '~/generated';

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

  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'],
  },
});
Puncte cheie:
  • isTool (boolean, implicit: false): Când este setat la true, funcția este înregistrată ca instrument și devine disponibilă pentru agenții AI și automatizările de fluxuri de lucru.
  • toolInputSchema (object, opțional): Un obiect JSON Schema care descrie parametrii pe care îi acceptă funcția dvs. Agenții AI folosesc această schemă pentru a înțelege ce intrări așteaptă instrumentul și pentru a valida apelurile. Dacă este omisă, schema are implicit valoarea { type: 'object', properties: {} } (fără parametri).
  • Funcțiile cu isTool: false (sau nedefinit) nu sunt expuse ca instrumente. Pot totuși fi executate direct sau apelate de alte funcții, dar nu vor apărea în descoperirea instrumentelor.
  • Denumierea instrumentelor: Când este expusă ca instrument, denumirea funcției este normalizată automat la logic_function_<name> (convertită la litere mici, iar caracterele non-alfanumerice sunt înlocuite cu caractere de subliniere). De exemplu, enrich-company devine logic_function_enrich_company.
  • Puteți combina isTool cu declanșatoare — o funcție poate fi atât un instrument (apelabilă de agenții AI), cât și declanșată de evenimente (cron, evenimente de bază de date, rute) în același timp.
Scrieți o description bună. Agenții AI se bazează pe câmpul description al funcției pentru a decide când să folosească instrumentul. Fiți specifici cu privire la ceea ce face instrumentul și când ar trebui apelat.

Componente Front

Componentele Front vă permit să construiți componente React personalizate care sunt randate în interfața Twenty. Utilizați defineFrontComponent() pentru a defini componente cu validare încorporată:
// src/my-widget.front-component.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,
});
Puncte cheie:
  • Componentele Front sunt componente React care sunt randate în contexte izolate în cadrul Twenty.
  • Folosiți sufixul de fișier *.front-component.tsx pentru detectare automată.
  • Câmpul component face referire la componenta React.
  • Componentele sunt construite și sincronizate automat în timpul yarn twenty app:dev.
Puteți crea componente Front noi în două moduri:
  • Generat: Rulați yarn twenty entity:add și alegeți opțiunea de a adăuga o componentă frontend nouă.
  • Manual: Creați un fișier nou *.front-component.tsx și folosiți defineFrontComponent().

Abilități

Abilitățile definesc instrucțiuni și capabilități reutilizabile pe care agenții AI le pot folosi în spațiul dvs. de lucru. Folosiți defineSkill() pentru a defini abilități cu validare încorporată:
// src/skills/example-skill.ts
import { defineSkill } from 'twenty-sdk';

export default defineSkill({
  universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
  name: 'sales-outreach',
  label: 'Abordare de vânzări',
  description: 'Ghidează agentul AI printr-un proces structurat de abordare de vânzări',
  icon: 'IconBrain',
  content: `Ești un asistent pentru abordare de vânzări. Când contactezi un potențial client:
1. Cercetează compania și noutățile recente
2. Identifică rolul potențialului client și probabilele puncte sensibile
3. Redactează un mesaj personalizat care face referire la detalii specifice
4. Păstrează un ton profesionist, dar conversațional`,
});
Puncte cheie:
  • name este un șir identificator unic pentru abilitate (se recomandă kebab-case).
  • label este numele lizibil afișat în interfața cu utilizatorul (UI).
  • content conține instrucțiunile abilității — acesta este textul pe care agentul AI îl folosește.
  • icon (opțional) setează pictograma afișată în UI.
  • description (opțional) oferă context suplimentar despre scopul abilității.
Puteți crea abilități noi în două moduri:
  • Generat: Rulați yarn twenty entity:add și alegeți opțiunea de a adăuga o abilitate nouă.
  • Manual: Creați un fișier nou și folosiți defineSkill(), urmând același model.

Client tipizat generat

Clientul tipizat este generat automat de yarn twenty app:dev și stocat în node_modules/twenty-sdk/generated, pe baza schemei spațiului tău de lucru. Folosiți-l în funcțiile dvs.:
import Twenty from '~/generated';

const client = new Twenty();
const { me } = await client.query({ me: { id: true, displayName: true } });
Clientul este regenerat automat de yarn twenty app:dev ori de câte ori obiectele sau câmpurile tale se schimbă.

Acreditări la runtime în funcțiile de logică

Când funcția rulează pe Twenty, platforma injectează acreditări ca variabile de mediu înainte de execuția codului:
  • TWENTY_API_URL: URL-ul de bază al API-ului Twenty către care țintește aplicația.
  • TWENTY_API_KEY: Cheie cu durată scurtă, limitată la rolul implicit de funcție al aplicației.
Notițe:
  • Nu trebuie să transmiteți URL-ul sau cheia API către clientul generat. Acesta citește TWENTY_API_URL și TWENTY_API_KEY din process.env la runtime.
  • Permisiunile cheii API sunt determinate de rolul referențiat în application-config.ts prin defaultRoleUniversalIdentifier. Acesta este rolul implicit folosit de funcțiile de logică ale aplicației.
  • Aplicațiile pot defini roluri pentru a urma principiul celui mai mic privilegiu. Acordați doar permisiunile de care au nevoie funcțiile, apoi setați defaultRoleUniversalIdentifier la identificatorul universal al acelui rol.

Exemplu Hello World

Explorați un exemplu minim, cap la cap, care demonstrează obiecte, funcții de logică, componente Front și declanșatoare multiple aici:

Configurare manuală (fără generator)

Deși recomandăm utilizarea create-twenty-app pentru cea mai bună experiență de început, puteți configura și un proiect manual. Nu instalați CLI-ul global. În schimb, adăugați twenty-sdk ca dependență locală și conectați un singur script în package.json-ul dvs.:
yarn add -D twenty-sdk
Apoi adăugați un script twenty:
{
  "scripts": {
    "twenty": "twenty"
  }
}
Acum poți rula toate comenzile prin yarn twenty <command>, de ex. yarn twenty app:dev, yarn twenty help, etc.

Depanare

  • Erori de autentificare: rulați yarn twenty auth:login și asigurați-vă că cheia API are permisiunile necesare.
  • Nu se poate conecta la server: verificați URL-ul API și că serverul Twenty este accesibil.
  • Types or client missing/outdated: restart yarn twenty app:dev — it auto-generates the typed client.
  • Modul dev nu sincronizează: asigurați-vă că yarn twenty app:dev rulează și că modificările nu sunt ignorate de mediul dvs.
Canal de ajutor pe Discord: https://discord.com/channels/1130383047699738754/1130386664812982322