i18n - docs translations (#18534)
Created by Github action --------- Co-authored-by: github-actions <github-actions@twenty.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
committed by
GitHub
parent
d1c95e380e
commit
982f0c4a4d
+1
-1
@@ -77,7 +77,7 @@ Per evitare [re-render](/l/it/developers/contribute/capabilities/frontend-develo
|
||||
|
||||
### Gestione dello stato
|
||||
|
||||
[Jotai](https://jotai.org/) gestisce lo stato.
|
||||
[Jotai](https://jotai.org/) handles state management.
|
||||
|
||||
Vedi [best practices](/l/it/developers/contribute/capabilities/frontend-development/best-practices-front#state-management) per ulteriori informazioni sulla gestione dello stato.
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: API
|
||||
description: Interroga e modifica i dati del tuo CRM in modo programmatico usando REST o GraphQL.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
Twenty è stato progettato per essere adatto agli sviluppatori, offrendo potenti API che si adattano al tuo modello di dati personalizzato. Forniamo quattro tipi distinti di API per soddisfare diverse esigenze di integrazione.
|
||||
|
||||
## Approccio incentrato sullo sviluppatore
|
||||
|
||||
Twenty genera API specifiche per il tuo modello di dati:
|
||||
|
||||
* **Nessun ID lungo richiesto**: Utilizza direttamente i nomi degli oggetti e dei campi negli endpoint
|
||||
* **Oggetti standard e personalizzati trattati allo stesso modo**: I tuoi oggetti personalizzati ricevono lo stesso trattamento API di quelli predefiniti
|
||||
* **Endpoint dedicati**: Ogni oggetto e campo ottiene il proprio endpoint API
|
||||
* **Documentazione personalizzata**: Generata specificamente per il modello di dati del tuo workspace
|
||||
|
||||
<Note>
|
||||
La documentazione personalizzata delle tue API è disponibile in **Impostazioni → API & Webhooks** dopo aver creato una chiave API. Poiché Twenty genera API che corrispondono al tuo modello di dati personalizzato, la documentazione è unica per il tuo workspace.
|
||||
</Note>
|
||||
|
||||
## I due tipi di API
|
||||
|
||||
### Core API
|
||||
|
||||
Accessibile su `/rest/` o `/graphql/`
|
||||
|
||||
Lavora con i tuoi **record** reali (i dati):
|
||||
|
||||
* Crea, leggi, aggiorna, elimina Persone, Aziende, Opportunità, ecc.
|
||||
* Interroga e filtra i dati
|
||||
* Gestisci le relazioni tra i record
|
||||
|
||||
### Metadata API
|
||||
|
||||
Accessibile su `/rest/metadata/` o `/metadata/`
|
||||
|
||||
Gestisci il tuo **workspace e il modello di dati**:
|
||||
|
||||
* Crea, modifica o elimina oggetti e campi
|
||||
* Configura le impostazioni del workspace
|
||||
* Definisci le relazioni tra oggetti
|
||||
|
||||
## REST vs GraphQL
|
||||
|
||||
Sia le API Core che le API Metadata sono disponibili nei formati REST e GraphQL:
|
||||
|
||||
| Formato | Operazioni disponibili |
|
||||
| ----------- | ------------------------------------------------------------------------------------- |
|
||||
| **REST** | CRUD, operazioni batch, upsert |
|
||||
| **GraphQL** | Stesse funzionalità + **upsert in batch**, query sulle relazioni in un'unica chiamata |
|
||||
|
||||
Scegli in base alle tue esigenze — entrambi i formati accedono agli stessi dati.
|
||||
|
||||
## Endpoint API
|
||||
|
||||
| Ambiente | URL di base |
|
||||
| ----------------- | ------------------------- |
|
||||
| **Cloud** | `https://api.twenty.com/` |
|
||||
| **Auto-ospitato** | `https://{your-domain}/` |
|
||||
|
||||
## Autenticazione
|
||||
|
||||
Ogni richiesta API richiede una chiave API nell'intestazione:
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
### Crea una chiave API
|
||||
|
||||
1. Vai a **Impostazioni → APIs & Webhooks**
|
||||
2. Fai clic su **+ Crea chiave**
|
||||
3. Configura:
|
||||
* **Nome**: Nome descrittivo per la chiave
|
||||
* **Data di scadenza**: Quando la chiave scade
|
||||
4. Fai clic su **Salva**
|
||||
5. **Copia subito** — la chiave viene mostrata una sola volta
|
||||
|
||||
<VimeoEmbed videoId="928786722" title="Creazione della chiave API" />
|
||||
|
||||
<Warning>
|
||||
La tua chiave API concede l'accesso a dati sensibili. Non condividerla con servizi non affidabili. Se compromessa, disabilitala immediatamente e generane una nuova.
|
||||
</Warning>
|
||||
|
||||
### Assegna un ruolo a una chiave API
|
||||
|
||||
Per una maggiore sicurezza, assegna un ruolo specifico per limitare l'accesso:
|
||||
|
||||
1. Vai a **Impostazioni → Ruoli**
|
||||
2. Fai clic sul ruolo da assegnare
|
||||
3. Apri la scheda **Assegnazione**
|
||||
4. In **Chiavi API**, fai clic su **+ Assegna alla chiave API**
|
||||
5. Seleziona la chiave API
|
||||
|
||||
La chiave erediterà le autorizzazioni di quel ruolo. Vedi [Autorizzazioni](/l/it/user-guide/permissions-access/capabilities/permissions) per i dettagli.
|
||||
|
||||
### Gestisci chiavi API
|
||||
|
||||
**Rigenera**: Impostazioni → APIs & Webhooks → Fai clic sulla chiave → **Rigenera**
|
||||
|
||||
**Elimina**: Impostazioni → APIs & Webhooks → Fai clic sulla chiave → **Elimina**
|
||||
|
||||
## Playground API
|
||||
|
||||
Testa le tue API direttamente nel browser con il nostro playground integrato — disponibile sia per **REST** sia per **GraphQL**.
|
||||
|
||||
### Accedi al Playground
|
||||
|
||||
1. Vai a **Impostazioni → APIs & Webhooks**
|
||||
2. Crea una chiave API (obbligatorio)
|
||||
3. Fai clic su **REST API** o **GraphQL API** per aprire il playground
|
||||
|
||||
### Cosa ottieni
|
||||
|
||||
* **Documentazione interattiva**: Generata per il tuo specifico modello di dati
|
||||
* **Test in tempo reale**: Esegui chiamate API reali sul tuo workspace
|
||||
* **Esploratore dello schema**: Sfoglia gli oggetti, i campi e le relazioni disponibili
|
||||
* **Generatore di richieste**: Crea query con completamento automatico
|
||||
|
||||
Il playground riflette i tuoi oggetti e campi personalizzati, quindi la documentazione è sempre accurata per il tuo workspace.
|
||||
|
||||
## Operazioni Batch
|
||||
|
||||
Sia REST che GraphQL supportano operazioni batch:
|
||||
|
||||
* **Dimensione batch**: Fino a 60 record per richiesta
|
||||
* **Operazioni**: Creare, aggiornare, eliminare più record
|
||||
|
||||
**Funzionalità esclusive di GraphQL:**
|
||||
|
||||
* **Upsert in batch**: Crea o aggiorna in un'unica chiamata
|
||||
* Usa nomi oggetto al plurale (ad es. `CreateCompanies` invece di `CreateCompany`)
|
||||
|
||||
## Limiti di frequenza delle API
|
||||
|
||||
Le richieste API sono limitate per garantire la stabilità della piattaforma:
|
||||
|
||||
| Limite | Valore |
|
||||
| -------------------- | ---------------------- |
|
||||
| **Richieste** | 100 chiamate al minuto |
|
||||
| **Dimensione batch** | 60 record per chiamata |
|
||||
|
||||
<Tip>
|
||||
Usa le operazioni batch per massimizzare il throughput — elabora fino a 60 record in una singola chiamata API invece di effettuare richieste individuali.
|
||||
</Tip>
|
||||
@@ -0,0 +1,689 @@
|
||||
---
|
||||
title: Creazione di app
|
||||
description: Definisci oggetti, funzioni logiche, componenti front-end e molto altro con il Twenty SDK.
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Le app sono attualmente in fase alfa. La funzionalità è funzionante ma ancora in evoluzione.
|
||||
</Warning>
|
||||
|
||||
## 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à](/l/it/developers/extend/apps/getting-started#entity-detection), devi usare `export default define<Entity>({...})` affinché le tue entità vengano rilevate:
|
||||
|
||||
| Funzione | Scopo |
|
||||
| -------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `defineApplication` | Configura i metadati dell'applicazione (obbligatorio, uno per app) |
|
||||
| `defineObject` | Definisci oggetti personalizzati con campi |
|
||||
| `defineLogicFunction` | Definisci funzioni logiche con handler |
|
||||
| `definePreInstallLogicFunction` | Definisci una funzione logica di pre-installazione (una per app) |
|
||||
| `definePostInstallLogicFunction` | Definisci una funzione logica di post-installazione (una per app) |
|
||||
| `defineFrontComponent` | Definisci componenti front-end per un'interfaccia utente personalizzata |
|
||||
| `defineRole` | Configura i permessi dei ruoli e l'accesso agli oggetti |
|
||||
| `defineField` | Estendi gli oggetti esistenti con campi aggiuntivi |
|
||||
| `defineView` | Definisci viste salvate per gli oggetti |
|
||||
| `defineNavigationMenuItem` | Definisci i link di navigazione della barra laterale |
|
||||
| `defineSkill` | Definisci 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:
|
||||
|
||||
```typescript
|
||||
// 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.
|
||||
|
||||
<Note>
|
||||
**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.
|
||||
</Note>
|
||||
|
||||
### 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:
|
||||
|
||||
```typescript
|
||||
// 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](#pre-install-functions) e [Funzioni di post-installazione](#post-install-functions).
|
||||
|
||||
#### 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:
|
||||
|
||||
```typescript
|
||||
// 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`](https://github.com/twentyhq/twenty/blob/main/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.
|
||||
|
||||
```typescript
|
||||
// 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`:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```bash filename="Terminal"
|
||||
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`:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```bash filename="Terminal"
|
||||
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
|
||||
|
||||
<Warning>
|
||||
**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:**
|
||||
```typescript
|
||||
const handler = async (params) => {
|
||||
const { param1, param2 } = params; // Direct access
|
||||
};
|
||||
```
|
||||
|
||||
**Dopo la v1.16:**
|
||||
```typescript
|
||||
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`.
|
||||
</Warning>
|
||||
|
||||
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`:
|
||||
|
||||
```typescript
|
||||
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à | Tipo | Descrizione |
|
||||
| ---------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `headers` | `Record<string, string \| undefined>` | Intestazioni HTTP (solo quelle elencate in `forwardedRequestHeaders`) |
|
||||
| `queryStringParameters` | `Record<string, string \| undefined>` | Parametri della query string (valori multipli uniti da virgole) |
|
||||
| `pathParameters` | `Record<string, string \| undefined>` | Parametri di percorso estratti dal pattern della route (ad es., `/users/:id` → `{ id: '123' }`) |
|
||||
| `body` | `object \| null` | Corpo della richiesta analizzato (JSON) |
|
||||
| `isBase64Encoded` | `boolean` | Indica se il corpo è codificato in base64 |
|
||||
| `requestContext.http.method` | `string` | Metodo HTTP (GET, POST, PUT, PATCH, DELETE) |
|
||||
| `requestContext.http.path` | `string` | Percorso 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`:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const signature = event.headers['x-webhook-signature'];
|
||||
const contentType = event.headers['content-type'];
|
||||
|
||||
// Validate webhook signature...
|
||||
return { received: true };
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
I nomi delle intestazioni vengono normalizzati in minuscolo. Accedile usando chiavi in minuscolo (ad esempio, `event.headers['content-type']`).
|
||||
</Note>
|
||||
|
||||
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](https://json-schema.org/):
|
||||
|
||||
```typescript
|
||||
// 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.
|
||||
|
||||
<Note>
|
||||
**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.
|
||||
</Note>
|
||||
|
||||
### 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:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
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](https://github.com/jaydenseric/graphql-multipart-request-spec) dietro le quinte.
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
uploadFile(
|
||||
fileBuffer: Buffer,
|
||||
filename: string,
|
||||
contentType: string,
|
||||
fieldMetadataUniversalIdentifier: string,
|
||||
): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
|
||||
```
|
||||
|
||||
| Parametro | Tipo | Descrizione |
|
||||
| ---------------------------------- | -------- | ------------------------------------------------------------------------ |
|
||||
| `fileBuffer` | `Buffer` | Il contenuto grezzo del file |
|
||||
| `filename` | `string` | Il nome del file (utilizzato per l'archiviazione e la visualizzazione) |
|
||||
| `contentType` | `string` | Tipo MIME del file (predefinito su `application/octet-stream` se omesso) |
|
||||
| `fieldMetadataUniversalIdentifier` | `string` | L'`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](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world).
|
||||
@@ -0,0 +1,233 @@
|
||||
---
|
||||
title: Per iniziare
|
||||
description: Crea la tua prima app Twenty in pochi minuti.
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Le app sono attualmente in fase alfa. La funzionalità è funzionante ma ancora in evoluzione.
|
||||
</Warning>
|
||||
|
||||
Le app ti permettono di estendere Twenty con oggetti, campi, funzioni logiche, competenze IA e componenti UI personalizzati — il tutto gestito come codice.
|
||||
|
||||
**Cosa puoi fare oggi:**
|
||||
|
||||
* Definisci oggetti e campi personalizzati come codice (modello dati gestito)
|
||||
* Crea funzioni logiche con trigger personalizzati (route HTTP, cron, eventi del database)
|
||||
* Definisci le abilità per gli agenti IA
|
||||
* Crea componenti front-end che vengono renderizzati all'interno della UI di Twenty
|
||||
* Distribuisci la stessa app su più spazi di lavoro
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
* Node.js 24+ e Yarn 4
|
||||
* Uno spazio di lavoro Twenty e una chiave API (creane una su https://app.twenty.com/settings/api-webhooks)
|
||||
|
||||
## Per iniziare
|
||||
|
||||
Crea una nuova app utilizzando lo scaffolder ufficiale, quindi autenticati e inizia a sviluppare:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Scaffold a new app (includes all examples by default)
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn twenty app:dev
|
||||
```
|
||||
|
||||
Lo strumento di scaffolding supporta due modalità per controllare quali file di esempio vengono inclusi:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Default (exhaustive): all examples (object, field, logic function, front component, view, navigation menu item, skill)
|
||||
npx create-twenty-app@latest my-app
|
||||
|
||||
# Minimal: only core files (application-config.ts and default-role.ts)
|
||||
npx create-twenty-app@latest my-app --minimal
|
||||
```
|
||||
|
||||
Da qui puoi:
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new entity to your application (guided)
|
||||
yarn twenty entity:add
|
||||
|
||||
# Watch your application's function logs
|
||||
yarn twenty function:logs
|
||||
|
||||
# Execute a function by name
|
||||
yarn twenty function:execute -n my-function -p '{"name": "test"}'
|
||||
|
||||
# Execute the pre-install function
|
||||
yarn twenty function:execute --preInstall
|
||||
|
||||
# Execute the post-install function
|
||||
yarn twenty function:execute --postInstall
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
yarn twenty app:uninstall
|
||||
|
||||
# Display commands' help
|
||||
yarn twenty help
|
||||
```
|
||||
|
||||
Vedi anche: le pagine di riferimento della CLI per [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) e [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
|
||||
|
||||
## Struttura del progetto (generata dallo scaffolder)
|
||||
|
||||
Quando esegui `npx create-twenty-app@latest my-twenty-app`, lo scaffolder:
|
||||
|
||||
* Copia un'applicazione base minimale in `my-twenty-app/`
|
||||
* Aggiunge una dipendenza locale `twenty-sdk` e la configurazione di Yarn 4
|
||||
* Crea file di configurazione e script collegati alla CLI `twenty`
|
||||
* Genera i file principali (configurazione dell'applicazione, ruolo predefinito per le funzioni logiche, funzioni di pre-installazione e post-installazione) più i file di esempio in base alla modalità di scaffolding
|
||||
|
||||
Un'app appena creata con la modalità predefinita `--exhaustive` si presenta così:
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
package.json
|
||||
yarn.lock
|
||||
.gitignore
|
||||
.nvmrc
|
||||
.yarnrc.yml
|
||||
.yarn/
|
||||
install-state.gz
|
||||
.oxlintrc.json
|
||||
tsconfig.json
|
||||
README.md
|
||||
public/ # Public assets folder (images, fonts, etc.)
|
||||
src/
|
||||
├── application-config.ts # Required - main application configuration
|
||||
├── roles/
|
||||
│ └── default-role.ts # Default role for logic functions
|
||||
├── objects/
|
||||
│ └── example-object.ts # Example custom object definition
|
||||
├── fields/
|
||||
│ └── example-field.ts # Example standalone field definition
|
||||
├── logic-functions/
|
||||
│ ├── hello-world.ts # Example logic function
|
||||
│ ├── pre-install.ts # Pre-install logic function
|
||||
│ └── post-install.ts # Post-install logic function
|
||||
├── front-components/
|
||||
│ └── hello-world.tsx # Example front component
|
||||
├── views/
|
||||
│ └── example-view.ts # Example saved view definition
|
||||
├── navigation-menu-items/
|
||||
│ └── example-navigation-menu-item.ts # Example sidebar navigation link
|
||||
└── skills/
|
||||
└── example-skill.ts # Example AI agent skill definition
|
||||
```
|
||||
|
||||
Con `--minimal`, vengono creati solo i file principali (`application-config.ts`, `roles/default-role.ts`, `logic-functions/pre-install.ts` e `logic-functions/post-install.ts`).
|
||||
|
||||
A livello generale:
|
||||
|
||||
* **package.json**: Dichiara il nome dell'app, la versione, i motori (Node 24+, Yarn 4) e aggiunge `twenty-sdk` più uno script `twenty` che delega alla CLI locale `twenty`. Esegui `yarn twenty help` per elencare tutti i comandi disponibili.
|
||||
* **.gitignore**: Ignora i file generati comuni come `node_modules`, `.yarn`, `generated/` (client tipizzato), `dist/`, `build/`, cartelle di coverage, file di log e file `.env*`.
|
||||
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Bloccano e configurano la toolchain Yarn 4 utilizzata dal progetto.
|
||||
* **.nvmrc**: Fissa la versione di Node.js prevista dal progetto.
|
||||
* **.oxlintrc.json** e **tsconfig.json**: Forniscono linting e configurazione TypeScript per i sorgenti TypeScript della tua app.
|
||||
* **README.md**: Un breve README nella radice dell'app con istruzioni di base.
|
||||
* **public/**: Una cartella per archiviare risorse pubbliche (immagini, font, file statici) che saranno servite con la tua applicazione. I file collocati qui vengono caricati durante la sincronizzazione e sono accessibili in fase di esecuzione.
|
||||
* **src/**: Il luogo principale in cui definisci la tua applicazione come codice
|
||||
|
||||
### Rilevamento delle entità
|
||||
|
||||
L'SDK rileva le entità analizzando i tuoi file TypeScript alla ricerca di chiamate **`export default define<Entity>({...})`**. Ogni tipo di entità ha una corrispondente funzione helper esportata da `twenty-sdk`:
|
||||
|
||||
| Funzione helper | Tipo di entità |
|
||||
| -------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| `defineObject` | Definizioni di oggetti personalizzati |
|
||||
| `defineLogicFunction` | Definizioni di funzioni logiche |
|
||||
| `definePreInstallLogicFunction` | Funzione logica di pre-installazione (viene eseguita prima dell'installazione) |
|
||||
| `definePostInstallLogicFunction` | Funzione logica di post-installazione (viene eseguita dopo l'installazione) |
|
||||
| `defineFrontComponent` | Definizioni dei componenti front-end |
|
||||
| `defineRole` | Definizioni di ruoli |
|
||||
| `defineField` | Estensioni di campo per oggetti esistenti |
|
||||
| `defineView` | Definizioni di viste salvate |
|
||||
| `defineNavigationMenuItem` | Definizioni delle voci del menu di navigazione |
|
||||
| `defineSkill` | Definizioni delle competenze degli agenti IA |
|
||||
|
||||
<Note>
|
||||
**La denominazione dei file è flessibile.** Il rilevamento delle entità è basato sull'AST — l'SDK esegue la scansione dei file sorgente alla ricerca del pattern `export default define<Entity>({...})`. Puoi organizzare file e cartelle come preferisci. Raggruppare per tipo di entità (ad es., `logic-functions/`, `roles/`) è solo una convenzione per l'organizzazione del codice, non un requisito.
|
||||
</Note>
|
||||
|
||||
Esempio di entità rilevata:
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
});
|
||||
```
|
||||
|
||||
Comandi successivi aggiungeranno altri file e cartelle:
|
||||
|
||||
* `yarn twenty app:dev` genererà automaticamente due client API tipizzati in `node_modules/twenty-sdk/generated`: `CoreApiClient` (per i dati dell'area di lavoro tramite `/graphql`) e `MetadataApiClient` (per la configurazione dell'area di lavoro e il caricamento di file tramite `/metadata`).
|
||||
* `yarn twenty entity:add` aggiungerà file di definizione delle entità sotto `src/` per i tuoi oggetti, funzioni, componenti front-end, ruoli, competenze e altro ancora.
|
||||
|
||||
## Autenticazione
|
||||
|
||||
La prima volta che esegui `yarn twenty auth:login`, ti verranno richiesti:
|
||||
|
||||
* URL dell'API (predefinito a http://localhost:3000 o al profilo dello spazio di lavoro corrente)
|
||||
* Chiave API
|
||||
|
||||
Le tue credenziali sono archiviate per utente in `~/.twenty/config.json`. Puoi mantenere più profili e passare da uno all'altro.
|
||||
|
||||
### Gestione delle aree di lavoro
|
||||
|
||||
```bash filename="Terminal"
|
||||
# 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
|
||||
```
|
||||
|
||||
Una volta che hai cambiato area di lavoro con `yarn twenty auth:switch`, tutti i comandi successivi utilizzeranno quell'area di lavoro per impostazione predefinita. Puoi comunque sovrascriverla temporaneamente con `--workspace <name>`.
|
||||
|
||||
## Configurazione manuale (senza lo scaffolder)
|
||||
|
||||
Sebbene consigliamo di utilizzare `create-twenty-app` per la migliore esperienza iniziale, puoi anche configurare un progetto manualmente. Non installare la CLI globalmente. Invece, aggiungi `twenty-sdk` come dipendenza locale e collega un unico script nel tuo package.json:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add -D twenty-sdk
|
||||
```
|
||||
|
||||
Quindi aggiungi uno script `twenty`:
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
"scripts": {
|
||||
"twenty": "twenty"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ora puoi eseguire tutti i comandi tramite `yarn twenty <command>`, ad es. `yarn twenty app:dev`, `yarn twenty help`, ecc.
|
||||
|
||||
## Risoluzione dei problemi
|
||||
|
||||
* Errori di autenticazione: esegui `yarn twenty auth:login` e assicurati che la tua chiave API abbia i permessi richiesti.
|
||||
* Impossibile connettersi al server: verifica l'URL dell'API e che il server Twenty sia raggiungibile.
|
||||
* Tipi o client mancanti/obsoleti: riavvia `yarn twenty app:dev` — genera automaticamente il client tipizzato.
|
||||
* Modalità di sviluppo non sincronizzata: assicurati che `yarn twenty app:dev` sia in esecuzione e che le modifiche non vengano ignorate dal tuo ambiente.
|
||||
|
||||
Canale di supporto su Discord: https://discord.com/channels/1130383047699738754/1130386664812982322
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: Pubblicazione
|
||||
description: Distribuisci la tua app Twenty nel marketplace oppure distribuiscila internamente.
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Le app sono attualmente in fase alfa. La funzionalità è funzionante ma ancora in evoluzione.
|
||||
</Warning>
|
||||
|
||||
## Panoramica
|
||||
|
||||
Una volta che la tua app è stata [compilata e testata localmente](/l/it/developers/extend/apps/building), hai due modalità per distribuirla:
|
||||
|
||||
* **Pubblica su npm** — elenca la tua app nel marketplace di Twenty affinché qualsiasi spazio di lavoro possa scoprirla e installarla.
|
||||
* **Esegui il push di un tarball** — distribuisci la tua app su un server Twenty specifico per uso interno senza renderla pubblicamente disponibile.
|
||||
|
||||
## Pubblicazione su npm
|
||||
|
||||
La pubblicazione su npm rende la tua app scopribile nel marketplace di Twenty. Qualsiasi spazio di lavoro Twenty può sfogliare, installare e aggiornare le app del marketplace direttamente dall'interfaccia utente.
|
||||
|
||||
### Requisiti
|
||||
|
||||
* Un account [npm](https://www.npmjs.com)
|
||||
* Il nome del tuo pacchetto **deve** usare il prefisso `twenty-app-` (es. `twenty-app-postcard-sender`)
|
||||
|
||||
### Passaggi
|
||||
|
||||
1. **Compila la tua app** — la CLI compila i tuoi sorgenti TypeScript e genera il manifest dell'applicazione:
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn twenty app:build
|
||||
```
|
||||
|
||||
2. **Pubblica su npm** — esegui il push del pacchetto compilato al registro npm:
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx twenty app:publish
|
||||
```
|
||||
|
||||
### Rilevamento automatico
|
||||
|
||||
I pacchetti con il prefisso `twenty-app-` vengono rilevati automaticamente dal catalogo del marketplace di Twenty. Una volta pubblicata, la tua app compare nel marketplace entro pochi minuti — non è richiesta alcuna registrazione o approvazione manuale.
|
||||
|
||||
### Pubblicazione con CI
|
||||
|
||||
Il progetto generato include un workflow di GitHub Actions che pubblica a ogni release. Esegue `app:build`, quindi `npm publish --provenance` dall'output di build:
|
||||
|
||||
```yaml filename=".github/workflows/publish.yml"
|
||||
name: Publish
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
registry-url: https://registry.npmjs.org
|
||||
- run: yarn install --immutable
|
||||
- run: npx twenty app:build
|
||||
- run: npm publish --provenance --access public
|
||||
working-directory: .twenty/output
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
```
|
||||
|
||||
Per altri sistemi CI (GitLab CI, CircleCI, ecc.), si applicano gli stessi tre comandi: `yarn install`, `npx twenty app:build`, quindi `npm publish` da `.twenty/output`.
|
||||
|
||||
<Tip>
|
||||
**npm provenance** è opzionale ma consigliata. La pubblicazione con `--provenance` aggiunge un badge di attendibilità alla tua scheda npm, consentendo agli utenti di verificare che il pacchetto sia stato creato a partire da uno specifico commit in una pipeline CI pubblica. Consulta la [documentazione su npm provenance](https://docs.npmjs.com/generating-provenance-statements) per le istruzioni di configurazione.
|
||||
</Tip>
|
||||
|
||||
## Distribuzione interna
|
||||
|
||||
Per le app che non vuoi rendere pubbliche — strumenti proprietari, integrazioni solo per l'azienda o build sperimentali — puoi eseguire il push di un tarball direttamente su un server Twenty.
|
||||
|
||||
### Push di un tarball
|
||||
|
||||
Compila la tua app e distribuiscila su un server specifico in un solo passaggio:
|
||||
|
||||
```bash filename="Terminal"
|
||||
npx twenty app:publish --server <server-url>
|
||||
```
|
||||
|
||||
Qualsiasi spazio di lavoro su quel server può quindi installare e aggiornare l'app dalla pagina delle impostazioni **Applicazioni**.
|
||||
|
||||
### Gestione delle versioni
|
||||
|
||||
Per rilasciare un aggiornamento:
|
||||
|
||||
1. Incrementa il campo `version` nel tuo `package.json`
|
||||
2. Esegui il push di un nuovo tarball con `npx twenty app:publish --server <server-url>`
|
||||
3. Gli spazi di lavoro su quel server vedranno l'aggiornamento disponibile nelle proprie impostazioni
|
||||
|
||||
<Note>
|
||||
Le app interne sono limitate al server su cui vengono caricate. Non compariranno nel marketplace pubblico e non potranno essere installate dagli spazi di lavoro su altri server.
|
||||
</Note>
|
||||
|
||||
## Categorie di app
|
||||
|
||||
Twenty organizza le app in tre categorie in base a come vengono distribuite:
|
||||
|
||||
| Categoria | Come funziona | Visibile nel marketplace? |
|
||||
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
|
||||
| **Sviluppo** | App in modalità di sviluppo locale eseguite tramite `yarn twenty app:dev`. Usate per la compilazione e i test. | No |
|
||||
| **Pubblicata** | App pubblicate su npm con il prefisso `twenty-app-`. Elencate nel marketplace per l'installazione da parte di qualsiasi spazio di lavoro. | Sì |
|
||||
| **Interna** | App distribuite tramite tarball su un server specifico. Disponibili solo per gli spazi di lavoro su quel server. | No |
|
||||
|
||||
<Tip>
|
||||
Inizia in modalità **Sviluppo** mentre crei la tua app. Quando è pronta, scegli **Pubblicata** (npm) per un'ampia distribuzione oppure **Interna** (tarball) per una distribuzione privata.
|
||||
</Tip>
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
title: Estendi
|
||||
description: Estendi le funzionalità di Twenty con API, webhook e app personalizzate.
|
||||
redirect: /developers/introduction
|
||||
---
|
||||
|
||||
<Frame>
|
||||
@@ -16,18 +15,18 @@ Twenty è progettato per essere estensibile. Usa le nostre API, i webhook e il f
|
||||
|
||||
* **API**: Interroga e modifica i dati del tuo CRM in modo programmatico usando REST o GraphQL
|
||||
* **Webhook**: Ricevi notifiche in tempo reale quando si verificano eventi in Twenty
|
||||
* **App**: Crea applicazioni personalizzate che estendono le capacità di Twenty - In arrivo!
|
||||
* **App**: Crea applicazioni personalizzate che estendono le capacità di Twenty
|
||||
|
||||
## Per iniziare
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="API" icon="codice" href="/l/it/developers/api">
|
||||
<Card title="API" icon="codice" href="/l/it/developers/extend/api">
|
||||
Connettiti a Twenty in modo programmatico
|
||||
</Card>
|
||||
<Card title="Webhooks" icon="bell" href="/l/it/developers/webhooks">
|
||||
<Card title="Webhooks" icon="bell" href="/l/it/developers/extend/webhooks">
|
||||
Ricevi notifiche sugli eventi in tempo reale
|
||||
</Card>
|
||||
<Card title="App" icon="puzzle-piece" href="/l/it/developers/apps/apps">
|
||||
Crea personalizzazioni come codice (Alpha)
|
||||
<Card title="App" icon="puzzle-piece" href="/l/it/developers/extend/apps/getting-started">
|
||||
Crea personalizzazioni come codice
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: Webhooks
|
||||
description: Ricevi notifiche in tempo reale quando si verificano eventi nel tuo CRM.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
I webhook inviano dati ai tuoi sistemi in tempo reale quando si verificano eventi in Twenty — senza necessità di polling. Usali per mantenere sincronizzati i sistemi esterni, attivare automazioni o inviare avvisi.
|
||||
|
||||
## Crea un Webhook
|
||||
|
||||
1. Vai a **Impostazioni → API e Webhook → Webhook**
|
||||
2. Clicca su **+ Crea webhook**
|
||||
3. Inserisci l'URL del tuo webhook (deve essere pubblicamente accessibile)
|
||||
4. Clicca su **Salva**
|
||||
|
||||
Il webhook si attiva immediatamente e inizia a inviare notifiche.
|
||||
|
||||
<VimeoEmbed videoId="928786708" title="Creazione di un webhook" />
|
||||
|
||||
### Gestisci Webhook
|
||||
|
||||
**Modifica**: Fai clic sul webhook → Aggiorna URL → **Salva**
|
||||
|
||||
**Elimina**: Fai clic sul webhook → **Elimina** → Conferma
|
||||
|
||||
## Eventi
|
||||
|
||||
Twenty invia webhook per questi tipi di eventi:
|
||||
|
||||
| Evento | Esempio |
|
||||
| --------------------- | ---------------------------------------------------------- |
|
||||
| **Record creato** | `person.created`, `company.created`, `note.created` |
|
||||
| **Record aggiornato** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **Record eliminato** | `person.deleted`, `company.deleted` |
|
||||
|
||||
Tutti i tipi di eventi vengono inviati all'URL del tuo webhook. Il filtraggio degli eventi potrebbe essere aggiunto nelle versioni future.
|
||||
|
||||
## Formato del payload
|
||||
|
||||
Ogni webhook invia una richiesta HTTP POST con un corpo JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "person.created",
|
||||
"data": {
|
||||
"id": "abc12345",
|
||||
"firstName": "Alice",
|
||||
"lastName": "Doe",
|
||||
"email": "alice@example.com",
|
||||
"createdAt": "2025-02-10T15:30:45Z",
|
||||
"createdBy": "user_123"
|
||||
},
|
||||
"timestamp": "2025-02-10T15:30:50Z"
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Descrizione |
|
||||
| ----------- | ---------------------------------------------------------- |
|
||||
| `event` | Cosa è successo (ad es., `person.created`) |
|
||||
| `data` | Il record completo che è stato creato/aggiornato/eliminato |
|
||||
| `timestamp` | Quando si è verificato l'evento (UTC) |
|
||||
|
||||
<Note>
|
||||
Rispondi con uno **status HTTP 2xx** (200-299) per confermare la ricezione. Le risposte non 2xx vengono registrate come errori di consegna.
|
||||
</Note>
|
||||
|
||||
## Convalida del Webhook
|
||||
|
||||
Twenty firma ogni richiesta webhook per motivi di sicurezza. Convalida le firme per assicurarti che le richieste siano autentiche.
|
||||
|
||||
### Intestazioni
|
||||
|
||||
| Intestazione | Descrizione |
|
||||
| ---------------------------- | ------------------------- |
|
||||
| `X-Twenty-Webhook-Signature` | Firma HMAC SHA256 |
|
||||
| `X-Twenty-Webhook-Timestamp` | Timestamp della richiesta |
|
||||
|
||||
### Passaggi di convalida
|
||||
|
||||
1. Ottieni il timestamp da `X-Twenty-Webhook-Timestamp`
|
||||
2. Crea la stringa: `{timestamp}:{JSON payload}`
|
||||
3. Calcola HMAC SHA256 usando il segreto del tuo webhook
|
||||
4. Confronta con `X-Twenty-Webhook-Signature`
|
||||
|
||||
### Esempio (Node.js)
|
||||
|
||||
```javascript
|
||||
const crypto = require("crypto");
|
||||
|
||||
const timestamp = req.headers["x-twenty-webhook-timestamp"];
|
||||
const payload = JSON.stringify(req.body);
|
||||
const secret = "your-webhook-secret";
|
||||
|
||||
const stringToSign = `${timestamp}:${payload}`;
|
||||
const expectedSignature = crypto
|
||||
.createHmac("sha256", secret)
|
||||
.update(stringToSign)
|
||||
.digest("hex");
|
||||
|
||||
const receivedSignature = req.headers["x-twenty-webhook-signature"];
|
||||
const isValid = crypto.timingSafeEqual(
|
||||
Buffer.from(expectedSignature, "hex"),
|
||||
Buffer.from(receivedSignature, "hex")
|
||||
);
|
||||
```
|
||||
|
||||
## Webhooks vs Workflows
|
||||
|
||||
| Metodo | Direzione | Caso d'uso |
|
||||
| -------------------------------- | --------- | ------------------------------------------------------------------------ |
|
||||
| **Webhooks** | OUT | Notifica automaticamente ai sistemi esterni qualsiasi modifica ai record |
|
||||
| **Workflow + HTTP Request** | OUT | Invia dati in uscita con logica personalizzata (filtri, trasformazioni) |
|
||||
| **Trigger webhook del Workflow** | IN | Ricevi dati in Twenty da sistemi esterni |
|
||||
|
||||
Per la ricezione di dati esterni, vedi [Configura un trigger webhook](/l/it/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
|
||||
@@ -5,28 +5,18 @@ description: Benvenuto nella documentazione per sviluppatori di Twenty, le tue r
|
||||
|
||||
import { CardTitle } from "/snippets/card-title.mdx"
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card href="/l/it/developers/api" icon="code">
|
||||
<CardTitle>API</CardTitle>
|
||||
Interroga e modifica i dati del tuo CRM con REST o GraphQL.
|
||||
<CardGroup cols={3}>
|
||||
<Card href="/l/it/developers/extend/extend" img="/images/user-guide/integrations/plug.png">
|
||||
<CardTitle>Estendi</CardTitle>
|
||||
Crea integrazioni con API, webhook e app personalizzate.
|
||||
</Card>
|
||||
|
||||
<Card href="/l/it/developers/webhooks" icon="bell">
|
||||
<CardTitle>Webhooks</CardTitle>
|
||||
Ricevi notifiche in tempo reale quando si verificano eventi.
|
||||
</Card>
|
||||
|
||||
<Card href="/l/it/developers/apps/apps" icon="puzzle-piece">
|
||||
<CardTitle>Apps</CardTitle>
|
||||
Crea applicazioni personalizzate che estendono le capacità di Twenty.
|
||||
</Card>
|
||||
|
||||
<Card href="/l/it/developers/self-host/self-host" icon="desktop">
|
||||
<Card href="/l/it/developers/self-host/self-host" img="/images/user-guide/what-is-twenty/20.png">
|
||||
<CardTitle>Self-hosting</CardTitle>
|
||||
Distribuisci e gestisci Twenty sulla tua infrastruttura.
|
||||
</Card>
|
||||
|
||||
<Card href="/l/it/developers/contribute/contribute" icon="github">
|
||||
<Card href="/l/it/developers/contribute/contribute" img="/images/user-guide/github/github-header.png">
|
||||
<CardTitle>Contribuisci</CardTitle>
|
||||
Unisciti alla nostra community open source e contribuisci a Twenty.
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user