i18n - docs translations (#17433)
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
committed by
GitHub
parent
c8c821a692
commit
2353bc62cc
@@ -1,147 +1,147 @@
|
||||
---
|
||||
title: APIs
|
||||
description: Query and modify your CRM data programmatically using REST or GraphQL.
|
||||
title: API
|
||||
description: Interrogez et modifiez vos données CRM par programmation à l'aide de REST ou de GraphQL.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
Twenty was built to be developer-friendly, offering powerful APIs that adapt to your custom data model. We provide four distinct API types to meet different integration needs.
|
||||
Twenty a été conçu pour être convivial pour les développeurs, offrant des API puissantes qui s'adaptent à votre modèle de données personnalisé. Nous fournissons quatre types d'API distincts pour répondre à différents besoins d'intégration.
|
||||
|
||||
## Developer-First Approach
|
||||
## Approche axée sur les développeurs
|
||||
|
||||
Twenty generates APIs specifically for your data model:
|
||||
Twenty génère des API spécifiquement pour votre modèle de données :
|
||||
|
||||
* **No long IDs required**: Use your object and field names directly in endpoints
|
||||
* **Standard and custom objects treated equally**: Your custom objects get the same API treatment as built-in ones
|
||||
* **Dedicated endpoints**: Each object and field gets its own API endpoint
|
||||
* **Custom documentation**: Generated specifically for your workspace's data model
|
||||
* **Pas besoin d'ID longs** : Utilisez directement les noms de vos objets et champs dans les points de terminaison
|
||||
* **Objets standard et personnalisés traités à égalité** : Vos objets personnalisés bénéficient du même traitement API que ceux intégrés
|
||||
* **Points de terminaison dédiés** : Chaque objet et champ a son propre point de terminaison API
|
||||
* **Documentation personnalisée** : Générée spécifiquement pour le modèle de données de votre espace de travail
|
||||
|
||||
<Note>
|
||||
Your personalized API documentation is available under **Settings → API & Webhooks** after creating an API key. Since Twenty generates APIs that match your custom data model, the documentation is unique to your workspace.
|
||||
Votre documentation API personnalisée est disponible sous **Paramètres → API & Webhooks** après la création d'une clé API. Comme Twenty génère des API qui correspondent à votre modèle de données personnalisé, la documentation est propre à votre espace de travail.
|
||||
</Note>
|
||||
|
||||
## The Two API Types
|
||||
## Les deux types d'API
|
||||
|
||||
### Core API
|
||||
### API principale
|
||||
|
||||
Accessed on `/rest/` or `/graphql/`
|
||||
Accessible sur `/rest/` ou `/graphql/`
|
||||
|
||||
Work with your actual **records** (the data):
|
||||
Travaillez avec vos **enregistrements** réels (les données) :
|
||||
|
||||
* Create, read, update, delete People, Companies, Opportunities, etc.
|
||||
* Query and filter data
|
||||
* Manage record relationships
|
||||
* Créer, lire, mettre à jour, supprimer People, Companies, Opportunities, etc.
|
||||
* Interroger et filtrer les données
|
||||
* Gérer les relations entre les enregistrements
|
||||
|
||||
### Metadata API
|
||||
### API de métadonnées
|
||||
|
||||
Accessed on `/rest/metadata/` or `/metadata/`
|
||||
Accessible sur `/rest/metadata/` ou `/metadata/`
|
||||
|
||||
Manage your **workspace and data model**:
|
||||
Gérez votre **espace de travail et votre modèle de données** :
|
||||
|
||||
* Create, modify, or delete objects and fields
|
||||
* Configure workspace settings
|
||||
* Define relationships between objects
|
||||
* Créer, modifier ou supprimer des objets et des champs
|
||||
* Configurer les paramètres de l'espace de travail
|
||||
* Définir les relations entre les objets
|
||||
|
||||
## REST vs GraphQL
|
||||
|
||||
Both Core and Metadata APIs are available in REST and GraphQL formats:
|
||||
Les API Core et Metadata sont disponibles aux formats REST et GraphQL :
|
||||
|
||||
| Format | Available Operations |
|
||||
| ----------- | ---------------------------------------------------------- |
|
||||
| **REST** | CRUD, batch operations, upserts |
|
||||
| **GraphQL** | Same + **batch upserts**, relationship queries in one call |
|
||||
| Format | Opérations disponibles |
|
||||
| ----------- | ----------------------------------------------------------------------- |
|
||||
| **REST** | CRUD, opérations par lot, upserts |
|
||||
| **GraphQL** | Identique + **upserts par lot**, requêtes de relations en un seul appel |
|
||||
|
||||
Choose based on your needs — both formats access the same data.
|
||||
Choisissez selon vos besoins — les deux formats accèdent aux mêmes données.
|
||||
|
||||
## API Endpoints
|
||||
## Points d'accès API
|
||||
|
||||
| Environment | Base URL |
|
||||
| --------------- | ------------------------- |
|
||||
| **Cloud** | `https://api.twenty.com/` |
|
||||
| **Self-Hosted** | `https://{your-domain}/` |
|
||||
| Environnement | URL de base |
|
||||
| ---------------- | ------------------------- |
|
||||
| **Cloud** | `https://api.twenty.com/` |
|
||||
| **Auto-hébergé** | `https://{your-domain}/` |
|
||||
|
||||
## Authentication
|
||||
## Authentification
|
||||
|
||||
Every API request requires an API key in the header:
|
||||
Chaque requête API nécessite une clé API dans l'en-tête :
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
### Create an API Key
|
||||
### Créer une clé API
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks**
|
||||
2. Click **+ Create key**
|
||||
3. Configure:
|
||||
* **Name**: Descriptive name for the key
|
||||
* **Expiration Date**: When the key expires
|
||||
4. Click **Save**
|
||||
5. **Copy immediately** — the key is only shown once
|
||||
1. Allez dans **Paramètres → API & Webhooks**
|
||||
2. Cliquez sur **+ Créer une clé**
|
||||
3. Configurer:
|
||||
* **Nom** : Nom descriptif pour la clé
|
||||
* **Date d'expiration** : Date à laquelle la clé expire
|
||||
4. Cliquez sur **Enregistrer**
|
||||
5. **Copiez immédiatement** — la clé n'est affichée qu'une seule fois
|
||||
|
||||
<VimeoEmbed videoId="928786722" title="Creating API key" />
|
||||
<VimeoEmbed videoId="928786722" title="Création d'une clé API" />
|
||||
|
||||
<Warning>
|
||||
Your API key grants access to sensitive data. Don't share it with untrusted services. If compromised, disable it immediately and generate a new one.
|
||||
Votre clé API donne accès à des données sensibles. Ne la partagez pas avec des services non fiables. Si elle est compromise, désactivez-la immédiatement et générez-en une nouvelle.
|
||||
</Warning>
|
||||
|
||||
### Assign a Role to an API Key
|
||||
### Attribuer un rôle à une clé API
|
||||
|
||||
For better security, assign a specific role to limit access:
|
||||
Pour une meilleure sécurité, attribuez un rôle spécifique pour limiter l'accès :
|
||||
|
||||
1. Go to **Settings → Roles**
|
||||
2. Click on the role to assign
|
||||
3. Open the **Assignment** tab
|
||||
4. Under **API Keys**, click **+ Assign to API key**
|
||||
5. Select the API key
|
||||
1. Allez dans **Paramètres → Rôles**
|
||||
2. Cliquez sur le rôle à attribuer
|
||||
3. Ouvrez l'onglet **Attribution**
|
||||
4. Sous **Clés API**, cliquez sur **+ Attribuer à une clé API**
|
||||
5. Sélectionnez la clé API
|
||||
|
||||
The key will inherit that role's permissions. See [Permissions](/l/fr/user-guide/permissions-access/capabilities/permissions) for details.
|
||||
La clé héritera des autorisations de ce rôle. Voir [Autorisations](/l/fr/user-guide/permissions-access/capabilities/permissions) pour plus de détails.
|
||||
|
||||
### Manage API Keys
|
||||
### Gérer les clés API
|
||||
|
||||
**Regenerate**: Settings → APIs & Webhooks → Click key → **Regenerate**
|
||||
**Régénérer** : Paramètres → API & Webhooks → Cliquez sur la clé → **Régénérer**
|
||||
|
||||
**Delete**: Settings → APIs & Webhooks → Click key → **Delete**
|
||||
**Supprimer** : Paramètres → API & Webhooks → Cliquez sur la clé → **Supprimer**
|
||||
|
||||
## API Playground
|
||||
## Bac à sable API
|
||||
|
||||
Test your APIs directly in the browser with our built-in playground — available for both **REST** and **GraphQL**.
|
||||
Testez vos API directement dans le navigateur avec notre bac à sable intégré — disponible pour **REST** et **GraphQL**.
|
||||
|
||||
### Access the Playground
|
||||
### Accéder au bac à sable
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks**
|
||||
2. Create an API key (required)
|
||||
3. Click on **REST API** or **GraphQL API** to open the playground
|
||||
1. Allez dans **Paramètres → API & Webhooks**
|
||||
2. Créer une clé API (obligatoire)
|
||||
3. Cliquez sur **REST API** ou **GraphQL API** pour ouvrir le bac à sable
|
||||
|
||||
### What You Get
|
||||
### Ce que vous obtenez
|
||||
|
||||
* **Interactive documentation**: Generated for your specific data model
|
||||
* **Live testing**: Execute real API calls against your workspace
|
||||
* **Schema explorer**: Browse available objects, fields, and relationships
|
||||
* **Request builder**: Construct queries with autocomplete
|
||||
* **Documentation interactive** : Générée pour votre modèle de données spécifique
|
||||
* **Tests en direct** : Exécutez de véritables appels API sur votre espace de travail
|
||||
* **Explorateur de schéma** : Parcourez les objets, champs et relations disponibles
|
||||
* **Générateur de requêtes** : Construisez des requêtes avec l'autocomplétion
|
||||
|
||||
The playground reflects your custom objects and fields, so documentation is always accurate for your workspace.
|
||||
Le bac à sable reflète vos objets et champs personnalisés, de sorte que la documentation est toujours précise pour votre espace de travail.
|
||||
|
||||
## Batch Operations
|
||||
## Opérations par Lot
|
||||
|
||||
Both REST and GraphQL support batch operations:
|
||||
REST et GraphQL prennent en charge les opérations par lot :
|
||||
|
||||
* **Batch size**: Up to 60 records per request
|
||||
* **Operations**: Create, update, delete multiple records
|
||||
* **Taille du lot** : Jusqu'à 60 enregistrements par requête
|
||||
* **Opérations** : Créer, mettre à jour, supprimer plusieurs enregistrements
|
||||
|
||||
**GraphQL-only features:**
|
||||
**Fonctionnalités spécifiques à GraphQL :**
|
||||
|
||||
* **Batch Upsert**: Create or update in one call
|
||||
* Use plural object names (e.g., `CreateCompanies` instead of `CreateCompany`)
|
||||
* **Upsert par lot** : Créer ou mettre à jour en un seul appel
|
||||
* Utilisez des noms d'objets au pluriel (par exemple, `CreateCompanies` au lieu de `CreateCompany`)
|
||||
|
||||
## Rate Limits
|
||||
## Limites de débit
|
||||
|
||||
API requests are throttled to ensure platform stability:
|
||||
Les requêtes API sont limitées pour garantir la stabilité de la plateforme :
|
||||
|
||||
| Limit | Value |
|
||||
| -------------- | -------------------- |
|
||||
| **Requests** | 100 calls per minute |
|
||||
| **Batch size** | 60 records per call |
|
||||
| Limite | Valeur |
|
||||
| ----------------- | ---------------------------- |
|
||||
| **Requêtes** | 100 appels par minute |
|
||||
| **Taille du lot** | 60 enregistrements par appel |
|
||||
|
||||
<Tip>
|
||||
Use batch operations to maximize throughput — process up to 60 records in a single API call instead of making individual requests.
|
||||
Utilisez les opérations par lot pour maximiser le débit — traitez jusqu'à 60 enregistrements dans un seul appel API au lieu d'effectuer des requêtes individuelles.
|
||||
</Tip>
|
||||
|
||||
@@ -1,81 +1,88 @@
|
||||
---
|
||||
title: Twenty Apps
|
||||
description: Build and manage Twenty customizations as code.
|
||||
title: Applications Twenty
|
||||
description: Créez et gérez les personnalisations Twenty sous forme de code.
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Apps are currently in alpha testing. The feature is functional but still evolving.
|
||||
Les applications sont actuellement en phase de test alpha. La fonctionnalité est fonctionnelle mais encore en évolution.
|
||||
</Warning>
|
||||
|
||||
## What Are Apps?
|
||||
## Que sont les applications ?
|
||||
|
||||
Apps let you build and manage Twenty customizations **as code**. Instead of configuring everything through the UI, you define your data model and serverless functions in code — making it faster to build, maintain, and roll out to multiple workspaces.
|
||||
Les applications vous permettent de créer et de gérer des personnalisations Twenty **sous forme de code**. Au lieu de tout configurer via l’interface utilisateur, vous définissez votre modèle de données et des fonctions sans serveur dans le code — ce qui accélère la création, la maintenance et le déploiement sur plusieurs espaces de travail.
|
||||
|
||||
**What you can do today:**
|
||||
**Ce que vous pouvez faire aujourd'hui:**
|
||||
|
||||
* Define custom objects and fields as code (managed data model)
|
||||
* Build serverless functions with custom triggers
|
||||
* Deploy the same app across multiple workspaces
|
||||
* Définir des objets et des champs personnalisés sous forme de code (modèle de données géré)
|
||||
* Créer des fonctions sans serveur avec des déclencheurs personnalisés
|
||||
* Déployer la même application sur plusieurs espaces de travail
|
||||
|
||||
**Coming soon:**
|
||||
**Bientôt disponible :**
|
||||
|
||||
* Custom UI layouts and components
|
||||
* Mises en page et composants d’interface utilisateur personnalisés
|
||||
|
||||
## Prerequisites
|
||||
## Prérequis
|
||||
|
||||
* Node.js 24+ and Yarn 4
|
||||
* A Twenty workspace and an API key (create one at https://app.twenty.com/settings/api-webhooks)
|
||||
* Node.js 24+ et Yarn 4
|
||||
* Un espace de travail Twenty et une clé API (créez-en une sur https://app.twenty.com/settings/api-webhooks)
|
||||
|
||||
## Getting Started
|
||||
## Prise en main
|
||||
|
||||
Create a new app using the official scaffolder, then authenticate and start developing:
|
||||
Créez une nouvelle application avec l’outil d’amorçage officiel, puis authentifiez-vous et commencez à développer :
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Scaffold a new app
|
||||
# Générez une nouvelle application
|
||||
npx create-twenty-app@latest my-twenty-app
|
||||
cd my-twenty-app
|
||||
|
||||
# Authenticate using your API key (you'll be prompted)
|
||||
yarn auth
|
||||
# Si vous n'utilisez pas yarn@4
|
||||
corepack enable
|
||||
yarn install
|
||||
|
||||
# Start dev mode: automatically syncs local changes to your workspace
|
||||
yarn dev
|
||||
# Authentifiez-vous avec votre clé API (une invite s'affichera)
|
||||
yarn auth:login
|
||||
|
||||
# Démarrez le mode développement : synchronise automatiquement les modifications locales avec votre espace de travail
|
||||
yarn app:dev
|
||||
```
|
||||
|
||||
From here you can:
|
||||
À partir d'ici, vous pouvez :
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Add a new entity to your application (guided)
|
||||
yarn create-entity
|
||||
yarn app:create-entity
|
||||
|
||||
# Generate a typed Twenty client and workspace entity types
|
||||
yarn generate
|
||||
yarn app:generate
|
||||
|
||||
# Run a one‑time sync (instead of watch mode)
|
||||
yarn sync
|
||||
yarn app:sync
|
||||
|
||||
# Watch your application's functions logs
|
||||
yarn logs
|
||||
yarn function:logs
|
||||
|
||||
# Execute a function by name
|
||||
yarn function:execute -n my-function -p '{"name": "test"}'
|
||||
|
||||
# Uninstall the application from the current workspace
|
||||
yarn uninstall
|
||||
yarn app:uninstall
|
||||
|
||||
# Display commands' help
|
||||
yarn help
|
||||
yarn app:help},{
|
||||
```
|
||||
|
||||
See also: the CLI reference pages for [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) and [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
|
||||
Voir aussi : les pages de référence CLI pour [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) et [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
|
||||
|
||||
## Project structure (scaffolded)
|
||||
## Structure du projet (générée)
|
||||
|
||||
When you run `npx create-twenty-app@latest my-twenty-app`, the scaffolder:
|
||||
Lorsque vous exécutez `npx create-twenty-app@latest my-twenty-app`, l’outil de scaffolding :
|
||||
|
||||
* Copies a minimal base application into `my-twenty-app/`
|
||||
* Adds a local `twenty-sdk` dependency and Yarn 4 configuration
|
||||
* Creates config files and scripts wired to the `twenty` CLI
|
||||
* Generates a default application config and a default function role
|
||||
* Copie une application de base minimale dans `my-twenty-app/`
|
||||
* Ajoute une dépendance locale à `twenty-sdk` et la configuration Yarn 4
|
||||
* Crée des fichiers de configuration et des scripts reliés à la CLI `twenty`
|
||||
* Génère une configuration d’application par défaut et un rôle de fonction par défaut
|
||||
|
||||
A freshly scaffolded app looks like this:
|
||||
Une application nouvellement générée ressemble à ceci :
|
||||
|
||||
```text filename="my-twenty-app/"
|
||||
my-twenty-app/
|
||||
@@ -85,79 +92,144 @@ my-twenty-app/
|
||||
.nvmrc
|
||||
.yarnrc.yml
|
||||
.yarn/
|
||||
releases/
|
||||
yarn-4.9.2.cjs
|
||||
install-state.gz
|
||||
eslint.config.mjs
|
||||
tsconfig.json
|
||||
README.md
|
||||
src/
|
||||
application.config.ts
|
||||
role.config.ts
|
||||
// your entities, actions, and other app files
|
||||
app/
|
||||
application.config.ts # Obligatoire - configuration principale de l'application
|
||||
default-function.role.ts # Rôle par défaut pour les fonctions serverless
|
||||
// vos entités (*.object.ts, *.function.ts, *.role.ts)
|
||||
utils/ # Optionnel - implémentations de gestionnaires et utilitaires
|
||||
```
|
||||
|
||||
At a high level:
|
||||
### Convention plutôt que configuration
|
||||
|
||||
* **package.json**: Declares the app name, version, engines (Node 24+, Yarn 4), and adds `twenty-sdk` plus scripts like `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall`, and `auth` that delegate to the local `twenty` CLI.
|
||||
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
|
||||
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
|
||||
* **.nvmrc**: Pins the Node.js version expected by the project.
|
||||
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your app’s TypeScript sources.
|
||||
* **README.md**: A short README in the app root with basic instructions.
|
||||
* **src/**: The main place where you define your application-as-code:
|
||||
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See “Application config” below.
|
||||
* `role.config.ts`: Default function role used by your serverless functions. See “Default function role” below.
|
||||
* Future entities, actions/functions, and any supporting code you add.
|
||||
Les applications adoptent une approche **convention plutôt que configuration** où les entités sont détectées par leur suffixe de fichier. Cela permet une organisation flexible dans le dossier `src/app/` :
|
||||
|
||||
Later commands will add more files and folders:
|
||||
| Suffixe de fichier | Type d’entité |
|
||||
| ------------------ | ------------------------------------- |
|
||||
| `*.object.ts` | Définitions d’objets personnalisés |
|
||||
| `*.function.ts` | Définitions de fonctions sans serveur |
|
||||
| `*.role.ts` | Définitions de rôles |
|
||||
|
||||
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
|
||||
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
|
||||
### Structures de dossiers prises en charge
|
||||
|
||||
## Authentication
|
||||
Vous pouvez organiser vos entités selon l’un des schémas suivants :
|
||||
|
||||
The first time you run `yarn auth`, you'll be prompted for:
|
||||
**Traditionnelle (par type) :**
|
||||
|
||||
* API URL (defaults to http://localhost:3000 or your current workspace profile)
|
||||
* API key
|
||||
```text
|
||||
src/app/
|
||||
├── application.config.ts
|
||||
├── objects/
|
||||
│ └── postCard.object.ts
|
||||
├── functions/
|
||||
│ └── createPostCard.function.ts
|
||||
└── roles/
|
||||
└── admin.role.ts
|
||||
```
|
||||
|
||||
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch using `--workspace <name>`.
|
||||
**Par fonctionnalité :**
|
||||
|
||||
Examples:
|
||||
```text
|
||||
src/app/
|
||||
├── application.config.ts
|
||||
└── post-card/
|
||||
├── postCard.object.ts
|
||||
├── createPostCard.function.ts
|
||||
└── postCardAdmin.role.ts
|
||||
```
|
||||
|
||||
**À plat :**
|
||||
|
||||
```text
|
||||
src/app/
|
||||
├── application.config.ts
|
||||
├── postCard.object.ts
|
||||
├── createPostCard.function.ts
|
||||
└── admin.role.ts
|
||||
```
|
||||
|
||||
Dans les grandes lignes :
|
||||
|
||||
* **package.json** : Déclare le nom de l’application, la version, les moteurs (Node 24+, Yarn 4), et ajoute `twenty-sdk` ainsi que des scripts comme `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall` et `auth` qui délèguent à la CLI locale `twenty`.
|
||||
* **.gitignore** : Ignore les artefacts courants tels que `node_modules`, `.yarn`, `generated/` (client typé), `dist/`, `build/`, les dossiers de couverture, les fichiers journaux et les fichiers `.env*`.
|
||||
* **yarn.lock**, **.yarnrc.yml**, **.yarn/** : Verrouillent et configurent la chaîne d’outils Yarn 4 utilisée par le projet.
|
||||
* **.nvmrc** : Fige la version de Node.js attendue par le projet.
|
||||
* **eslint.config.mjs** et **tsconfig.json** : Fournissent la configuration de linting et TypeScript pour les sources TypeScript de votre application.
|
||||
* **README.md** : Un bref README à la racine de l’application avec des instructions de base.
|
||||
* **src/app/** : L’endroit principal où vous définissez votre application sous forme de code :
|
||||
* `application.config.ts` : Configuration globale de votre application (métadonnées et liaisons d’exécution). Voir « Configuration de l’application » ci-dessous.
|
||||
* `*.role.ts` : Définitions de rôles utilisées par vos fonctions sans serveur. Voir « Rôle de fonction par défaut » ci-dessous.
|
||||
* `*.object.ts` : Définitions d’objets personnalisés.
|
||||
* `*.function.ts` : Définitions de fonctions sans serveur.
|
||||
* **src/utils/** : Dossier facultatif pour les implémentations de gestionnaires et utilitaires.
|
||||
|
||||
Des commandes ultérieures ajouteront d’autres fichiers et dossiers :
|
||||
|
||||
* `yarn app:generate` créera un dossier `generated/` (client Twenty typé + types de l’espace de travail).
|
||||
* `yarn app:create-entity` ajoutera des fichiers de définition d’entité sous `src/app/` pour vos objets, fonctions ou rôles personnalisés.
|
||||
l
|
||||
|
||||
## Authentification
|
||||
|
||||
La première fois que vous exécutez `yarn auth:login`, il vous sera demandé :
|
||||
|
||||
* URL de l’API (par défaut http://localhost:3000 ou votre profil d’espace de travail actuel)
|
||||
* Clé API
|
||||
|
||||
Vos identifiants sont stockés par utilisateur dans `~/.twenty/config.json`. Vous pouvez gérer plusieurs profils et basculer entre eux.
|
||||
|
||||
### Gestion des espaces de travail
|
||||
|
||||
```bash filename="Terminal"
|
||||
# Login interactively (recommended)
|
||||
yarn auth
|
||||
yarn auth:login
|
||||
|
||||
# Use a specific workspace profile
|
||||
yarn auth --workspace my-custom-workspace
|
||||
# Login to a specific workspace profile
|
||||
yarn auth:login --workspace my-custom-workspace
|
||||
|
||||
# List all configured workspaces
|
||||
yarn auth:list
|
||||
|
||||
# Switch the default workspace (interactive)
|
||||
yarn auth:switch
|
||||
|
||||
# Switch to a specific workspace
|
||||
yarn auth:switch production
|
||||
|
||||
# Check current authentication status
|
||||
yarn auth:status
|
||||
```
|
||||
|
||||
## Use the SDK resources (types & config)
|
||||
Une fois que vous avez changé d'espace de travail avec `auth:switch`, toutes les commandes suivantes utiliseront cet espace de travail par défaut. Vous pouvez toujours le surcharger temporairement avec `--workspace <name>`.
|
||||
|
||||
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
|
||||
## Utiliser les ressources du SDK (types et configuration)
|
||||
|
||||
### Defining objects
|
||||
Le paquet twenty-sdk fournit des blocs de construction typés et des fonctions utilitaires que vous utilisez dans votre application. Voici les éléments clés que vous manipulerez le plus souvent.
|
||||
|
||||
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
|
||||
### Fonctions utilitaires
|
||||
|
||||
Here is an example `postCard` object from the Hello World app:
|
||||
Le SDK fournit quatre fonctions utilitaires avec validation intégrée pour définir les entités de votre application :
|
||||
|
||||
| Fonction | Objectif |
|
||||
| ------------------ | ---------------------------------------------------------- |
|
||||
| `defineApp()` | Configurer les métadonnées de l’application |
|
||||
| `defineObject()` | Définir des objets personnalisés avec des champs |
|
||||
| `defineFunction()` | Définir des fonctions sans serveur avec des gestionnaires |
|
||||
| `defineRole()` | Configurer les autorisations de rôle et l’accès aux objets |
|
||||
|
||||
Ces fonctions valident votre configuration à l’exécution et offrent une meilleure autocomplétion IDE et une sécurité de typage accrue.
|
||||
|
||||
### Définir des objets
|
||||
|
||||
Les objets personnalisés décrivent à la fois le schéma et le comportement des enregistrements dans votre espace de travail. Utilisez `defineObject()` pour définir des objets avec validation intégrée :
|
||||
|
||||
```typescript
|
||||
import { type Note } from '../../generated';
|
||||
|
||||
import {
|
||||
type AddressField,
|
||||
Field,
|
||||
FieldType,
|
||||
type FullNameField,
|
||||
Object,
|
||||
OnDeleteAction,
|
||||
Relation,
|
||||
RelationType,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk';
|
||||
// src/app/postCard.object.ts
|
||||
import { defineObject, FieldType } from 'twenty-sdk';
|
||||
|
||||
enum PostCardStatus {
|
||||
DRAFT = 'DRAFT',
|
||||
@@ -166,176 +238,186 @@ enum PostCardStatus {
|
||||
RETURNED = 'RETURNED',
|
||||
}
|
||||
|
||||
@Object({
|
||||
export default defineObject({
|
||||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||||
nameSingular: 'postCard',
|
||||
namePlural: 'postCards',
|
||||
labelSingular: 'Post card',
|
||||
labelPlural: 'Post cards',
|
||||
description: ' A post card object',
|
||||
labelSingular: 'Post Card',
|
||||
labelPlural: 'Post Cards',
|
||||
description: 'A post card object',
|
||||
icon: 'IconMail',
|
||||
})
|
||||
export class PostCard {
|
||||
@Field({
|
||||
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
|
||||
type: FieldType.TEXT,
|
||||
label: 'Content',
|
||||
description: "Postcard's content",
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
content: string;
|
||||
|
||||
@Field({
|
||||
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
|
||||
type: FieldType.FULL_NAME,
|
||||
label: 'Recipient name',
|
||||
icon: 'IconUser',
|
||||
})
|
||||
recipientName: FullNameField;
|
||||
|
||||
@Field({
|
||||
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
|
||||
type: FieldType.ADDRESS,
|
||||
label: 'Recipient address',
|
||||
icon: 'IconHome',
|
||||
})
|
||||
recipientAddress: AddressField;
|
||||
|
||||
@Field({
|
||||
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
|
||||
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' },
|
||||
],
|
||||
})
|
||||
status: PostCardStatus;
|
||||
|
||||
@Relation({
|
||||
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
|
||||
type: RelationType.ONE_TO_MANY,
|
||||
label: 'Notes',
|
||||
icon: 'IconComment',
|
||||
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
|
||||
onDelete: OnDeleteAction.CASCADE,
|
||||
})
|
||||
notes: Note[];
|
||||
|
||||
@Field({
|
||||
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
|
||||
type: FieldType.DATE_TIME,
|
||||
label: 'Delivered at',
|
||||
icon: 'IconCheck',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
})
|
||||
deliveredAt?: Date;
|
||||
}
|
||||
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,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Key points:
|
||||
Points clés :
|
||||
|
||||
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
|
||||
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
|
||||
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
|
||||
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
|
||||
* Utilisez `defineObject()` pour une validation intégrée et une meilleure prise en charge par l’IDE.
|
||||
* Le `universalIdentifier` doit être unique et stable entre les déploiements.
|
||||
* Chaque champ nécessite un `name`, un `type`, un `label` et son propre `universalIdentifier` stable.
|
||||
* Le tableau `fields` est facultatif — vous pouvez définir des objets sans champs personnalisés.
|
||||
* Vous pouvez générer de nouveaux objets avec `yarn app:create-entity`, qui vous guide à travers le nommage, les champs et les relations.
|
||||
|
||||
### Application config (application.config.ts)
|
||||
<Note>
|
||||
**Les champs de base sont créés automatiquement.** Lorsque vous définissez un objet personnalisé, Twenty ajoute automatiquement des champs standard tels que `name`, `createdAt`, `updatedAt`, `createdBy`, `position` et `deletedAt`. Vous n'avez pas besoin de les définir dans votre tableau `fields` — ajoutez uniquement vos champs personnalisés.
|
||||
</Note>
|
||||
|
||||
Every app has a single `application.config.ts` file that describes:
|
||||
<Accordion title="Alternative : syntaxe basée sur des décorateurs">
|
||||
Vous pouvez également définir des objets à l’aide de décorateurs TypeScript. Cette approche utilise une syntaxe basée sur les classes avec les décorateurs `@Object`, `@Field` et `@Relation` :
|
||||
|
||||
* **Who the app is**: identifiers, display name, and description.
|
||||
* **How its functions run**: which role they use for permissions.
|
||||
* **(Optional) variables**: key–value pairs exposed to your functions as environment variables.
|
||||
```typescript
|
||||
import {
|
||||
type AddressField,
|
||||
Field,
|
||||
FieldType,
|
||||
type FullNameField,
|
||||
Object,
|
||||
OnDeleteAction,
|
||||
Relation,
|
||||
RelationType,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
|
||||
} from 'twenty-sdk';
|
||||
import { type Note } from '../../generated';
|
||||
|
||||
When you scaffold a new app, you start with a minimal config:
|
||||
@Object({
|
||||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||||
nameSingular: 'postCard',
|
||||
namePlural: 'postCards',
|
||||
labelSingular: 'Post card',
|
||||
labelPlural: 'Post cards',
|
||||
description: 'A post card object',
|
||||
icon: 'IconMail',
|
||||
})
|
||||
export class PostCard {
|
||||
@Field({
|
||||
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
|
||||
type: FieldType.TEXT,
|
||||
label: 'Content',
|
||||
description: "Postcard's content",
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
content: string;
|
||||
|
||||
@Relation({
|
||||
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
|
||||
type: RelationType.ONE_TO_MANY,
|
||||
label: 'Notes',
|
||||
icon: 'IconComment',
|
||||
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
|
||||
onDelete: OnDeleteAction.CASCADE,
|
||||
})
|
||||
notes: Note[];
|
||||
}
|
||||
```
|
||||
|
||||
Remarque : l’approche avec décorateurs requiert `experimentalDecorators` dans votre configuration TypeScript.
|
||||
</Accordion>
|
||||
|
||||
### Configuration de l’application (application.config.ts)
|
||||
|
||||
Chaque application dispose d’un seul fichier `application.config.ts` qui décrit :
|
||||
|
||||
* **Identité de l’application** : identifiants, nom d’affichage et description.
|
||||
* **Exécution des fonctions** : le rôle utilisé pour les autorisations.
|
||||
* **Variables (facultatif)** : paires clé–valeur exposées à vos fonctions en tant que variables d’environnement.
|
||||
|
||||
Utilisez `defineApp()` pour définir la configuration de votre application :
|
||||
|
||||
```typescript
|
||||
import { type ApplicationConfig } from 'twenty-sdk';
|
||||
// src/app/application.config.ts
|
||||
import { defineApp } from 'twenty-sdk';
|
||||
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
|
||||
|
||||
const config: ApplicationConfig = {
|
||||
universalIdentifier: '<generated-app-uuid>',
|
||||
export default defineApp({
|
||||
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
|
||||
displayName: 'My Twenty App',
|
||||
description: 'My first Twenty app',
|
||||
functionRoleUniversalIdentifier: '<generated-role-uuid>',
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
|
||||
|
||||
```typescript
|
||||
import { type ApplicationConfig } from 'twenty-sdk';
|
||||
|
||||
const config: ApplicationConfig = {
|
||||
universalIdentifier: '<your-app-uuid>',
|
||||
displayName: 'My App',
|
||||
description: 'What your app does',
|
||||
icon: 'IconWorld', // Choose an icon by name
|
||||
icon: 'IconWorld',
|
||||
applicationVariables: {
|
||||
DEFAULT_RECIPIENT_NAME: {
|
||||
universalIdentifier: '<uuid>',
|
||||
description: 'Default recipient used by functions',
|
||||
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
|
||||
description: 'Default recipient name for postcards',
|
||||
value: 'Jane Doe',
|
||||
isSecret: false,
|
||||
},
|
||||
},
|
||||
functionRoleUniversalIdentifier: '<your-role-uuid>',
|
||||
};
|
||||
|
||||
export default config;
|
||||
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
```
|
||||
|
||||
Notes:
|
||||
Notes :
|
||||
|
||||
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
|
||||
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
|
||||
* Les champs `universalIdentifier` sont des identifiants déterministes que vous possédez ; générez-les une fois et conservez-les stables entre les synchronisations.
|
||||
* `applicationVariables` deviennent des variables d’environnement pour vos fonctions (par exemple, `DEFAULT_RECIPIENT_NAME` est disponible sous `process.env.DEFAULT_RECIPIENT_NAME`).
|
||||
* `functionRoleUniversalIdentifier` doit correspondre au rôle que vous définissez dans votre fichier `*.role.ts` (voir ci-dessous).
|
||||
|
||||
#### Roles and permissions
|
||||
#### Rôles et autorisations
|
||||
|
||||
Applications can define roles that encapsulate permissions on your workspace’s objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your app’s serverless functions.
|
||||
Les applications peuvent définir des rôles qui encapsulent des autorisations sur les objets et actions de votre espace de travail. Le champ `functionRoleUniversalIdentifier` dans `application.config.ts` désigne le rôle par défaut utilisé par les fonctions sans serveur de votre application.
|
||||
|
||||
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
|
||||
* The typed client will be restricted to the permissions granted to that role.
|
||||
* Follow least‑privilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
|
||||
* La clé API d’exécution injectée sous `TWENTY_API_KEY` est dérivée de ce rôle de fonction par défaut.
|
||||
* Le client typé sera limité aux autorisations accordées à ce rôle.
|
||||
* Appliquez le principe du moindre privilège : créez un rôle dédié avec uniquement les autorisations nécessaires à vos fonctions, puis référencez son identifiant universel.
|
||||
|
||||
##### Default function role (role.config.ts)
|
||||
##### Rôle de fonction par défaut (\*.role.ts)
|
||||
|
||||
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
|
||||
Lorsque vous générez une nouvelle application, la CLI crée également un fichier de rôle par défaut. Utilisez `defineRole()` pour définir des rôles avec validation intégrée :
|
||||
|
||||
```typescript
|
||||
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
|
||||
// src/app/default-function.role.ts
|
||||
import { defineRole, PermissionFlag } from 'twenty-sdk';
|
||||
|
||||
export const functionRole: RoleConfig = {
|
||||
universalIdentifier: '<generated-role-uuid>',
|
||||
label: 'My Twenty App default function role',
|
||||
description: 'My Twenty App default function role',
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: false,
|
||||
};
|
||||
```
|
||||
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
|
||||
'b648f87b-1d26-4961-b974-0908fd991061';
|
||||
|
||||
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
|
||||
|
||||
* **role.config.ts** defines what the default function role can do.
|
||||
* **application.config.ts** points to that role so your functions inherit its permissions.
|
||||
|
||||
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
|
||||
|
||||
```typescript
|
||||
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
|
||||
|
||||
export const functionRole: RoleConfig = {
|
||||
universalIdentifier: '<your-role-uuid>',
|
||||
export default defineRole({
|
||||
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
label: 'Default function role',
|
||||
description: 'Default role for function Twenty client',
|
||||
canReadAllObjectRecords: false,
|
||||
@@ -363,41 +445,41 @@ export const functionRole: RoleConfig = {
|
||||
canUpdateFieldValue: false,
|
||||
},
|
||||
],
|
||||
permissionFlags: ['APPLICATIONS'],
|
||||
};
|
||||
permissionFlags: [PermissionFlag.APPLICATIONS],
|
||||
});
|
||||
```
|
||||
|
||||
Notes:
|
||||
Le `universalIdentifier` de ce rôle est ensuite référencé dans `application.config.ts` en tant que `functionRoleUniversalIdentifier`. En d’autres termes :
|
||||
|
||||
* Start from the scaffolded role, then progressively restrict it following least‑privilege.
|
||||
* Replace the `objectPermissions` and `fieldPermissions` with the objects/fields your functions need.
|
||||
* `permissionFlags` control access to platform-level capabilities. Keep them minimal; add only what you need.
|
||||
* See a working example in the Hello World app: [`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).
|
||||
* **\*.role.ts** définit ce que le rôle de fonction par défaut peut faire.
|
||||
* **application.config.ts** pointe vers ce rôle afin que vos fonctions héritent de ses autorisations.
|
||||
|
||||
### Serverless function config and entrypoint
|
||||
Notes :
|
||||
|
||||
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
|
||||
* Partez du rôle généré, puis restreignez-le progressivement en suivant le principe du moindre privilège.
|
||||
* Remplacez `objectPermissions` et `fieldPermissions` par les objets/champs dont vos fonctions ont besoin.
|
||||
* `permissionFlags` contrôlent l’accès aux capacités au niveau de la plateforme. Gardez-les au minimum ; n’ajoutez que ce dont vous avez besoin.
|
||||
* Voir un exemple fonctionnel dans l’application 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).
|
||||
|
||||
### Configuration et point d’entrée des fonctions sans serveur
|
||||
|
||||
Chaque fichier de fonction utilise `defineFunction()` pour exporter une configuration avec un gestionnaire et des déclencheurs facultatifs. Utilisez le suffixe de fichier `*.function.ts` pour la détection automatique.
|
||||
|
||||
```typescript
|
||||
// src/actions/create-new-post-card.ts
|
||||
import type {
|
||||
FunctionConfig,
|
||||
DatabaseEventPayload,
|
||||
ObjectRecordCreateEvent,
|
||||
CronPayload,
|
||||
} from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '../generated';
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '../../generated';
|
||||
|
||||
// main handler can accept parameters from route, cron, or database events
|
||||
export const main = async (
|
||||
const handler = async (
|
||||
params:
|
||||
| { name?: string }
|
||||
| RoutePayload
|
||||
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
|
||||
| CronPayload,
|
||||
) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const name = 'name' in params
|
||||
? params.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
|
||||
const result = await client.mutation({
|
||||
@@ -410,14 +492,15 @@ export const main = async (
|
||||
return result;
|
||||
};
|
||||
|
||||
export const config: FunctionConfig = {
|
||||
universalIdentifier: '<function-uuid>',
|
||||
export default defineFunction({
|
||||
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
|
||||
name: 'create-new-post-card',
|
||||
timeoutSeconds: 2,
|
||||
handler,
|
||||
triggers: [
|
||||
// Public HTTP route trigger '/s/post-card/create'
|
||||
{
|
||||
universalIdentifier: '<route-trigger-uuid>',
|
||||
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
|
||||
type: 'route',
|
||||
path: '/post-card/create',
|
||||
httpMethod: 'GET',
|
||||
@@ -425,39 +508,137 @@ export const config: FunctionConfig = {
|
||||
},
|
||||
// Cron trigger (CRON pattern)
|
||||
{
|
||||
universalIdentifier: '<cron-trigger-uuid>',
|
||||
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
|
||||
type: 'cron',
|
||||
pattern: '0 0 1 1 *',
|
||||
},
|
||||
// Database event trigger
|
||||
{
|
||||
universalIdentifier: '<db-trigger-uuid>',
|
||||
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
|
||||
type: 'databaseEvent',
|
||||
eventName: 'person.created',
|
||||
eventName: 'person.updated',
|
||||
updatedFields: ['name'],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Types de déclencheurs courants :
|
||||
|
||||
* **route** : Expose votre fonction sur un chemin et une méthode HTTP **sous l’endpoint `/s/`** :
|
||||
|
||||
> p. ex. `path: '/post-card/create',` -> appel sur `<APP_URL>/s/post-card/create`
|
||||
|
||||
* **cron** : Exécute votre fonction selon une planification à l’aide d’une expression CRON.
|
||||
* **databaseEvent**: S'exécute lors des événements du cycle de vie des objets de l'espace de travail. Lorsque l'opération de l'événement est `updated`, des champs spécifiques à surveiller peuvent être spécifiés dans le tableau `updatedFields`. S'il est laissé indéfini ou vide, toute mise à jour déclenchera la fonction.
|
||||
|
||||
> p. ex. `person.updated`
|
||||
|
||||
Notes :
|
||||
|
||||
* Le tableau `triggers` est facultatif. Les fonctions sans déclencheurs peuvent servir de fonctions utilitaires appelées par d’autres fonctions.
|
||||
* Vous pouvez combiner plusieurs types de déclencheurs dans une seule fonction.
|
||||
|
||||
### Charge utile du déclencheur de route
|
||||
|
||||
<Warning>
|
||||
**Changement incompatible (v1.16, janvier 2026):** Le format de la charge utile du déclencheur de route a changé. Avant la v1.16, les paramètres de requête, les paramètres de chemin et le corps de la requête étaient envoyés directement en tant que charge utile. À partir de la v1.16, ils sont imbriqués dans un objet `RoutePayload` structuré.
|
||||
|
||||
**Avant la v1.16 :**
|
||||
|
||||
```typescript
|
||||
const handler = async (params) => {
|
||||
const { param1, param2 } = params; // Direct access
|
||||
};
|
||||
```
|
||||
|
||||
**Après 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;
|
||||
};
|
||||
```
|
||||
|
||||
**Pour migrer les fonctions existantes :** Mettez à jour votre gestionnaire pour déstructurer à partir de `event.body`, `event.queryStringParameters` ou `event.pathParameters` plutôt que directement à partir de l'objet params.
|
||||
</Warning>
|
||||
|
||||
Lorsqu'un déclencheur de route appelle votre fonction, elle reçoit un objet `RoutePayload` qui suit le format AWS HTTP API v2. Importez le type depuis `twenty-sdk` :
|
||||
|
||||
```typescript
|
||||
import { defineFunction, 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' };
|
||||
};
|
||||
```
|
||||
|
||||
Common trigger types:
|
||||
Le type `RoutePayload` a la structure suivante :
|
||||
|
||||
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
|
||||
| Nom de la propriété | Type | Description |
|
||||
| ---------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| `headers` | `Record<string, string \| undefined>` | En-têtes HTTP (uniquement ceux répertoriés dans `forwardedRequestHeaders`) |
|
||||
| `queryStringParameters` | `Record<string, string \| undefined>` | Paramètres de la chaîne de requête (plusieurs valeurs séparées par des virgules) |
|
||||
| `pathParameters` | `Record<string, string \| undefined>` | Paramètres de chemin extraits du modèle de route (p. ex., `/users/:id` → `{ id: '123' }`) |
|
||||
| `corps du message` | `object \| null` | Corps de la requête analysé (JSON) |
|
||||
| `isBase64Encoded` | `booléen` | Indique si le corps est encodé en base64 |
|
||||
| `requestContext.http.method` | `string` | Méthode HTTP (GET, POST, PUT, PATCH, DELETE) |
|
||||
| `requestContext.http.path` | `string` | Chemin de la requête brut |
|
||||
|
||||
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
|
||||
### Transfert des en-têtes HTTP
|
||||
|
||||
* cron: Runs your function on a schedule using a CRON expression.
|
||||
* databaseEvent: Runs on workspace object lifecycle events
|
||||
Par défaut, les en-têtes HTTP des requêtes entrantes ne sont pas transmis à votre fonction sans serveur pour des raisons de sécurité. Pour accéder à des en-têtes spécifiques, listez-les explicitement dans le tableau `forwardedRequestHeaders` :
|
||||
|
||||
> e.g. `person.created`
|
||||
```typescript
|
||||
export default defineFunction({
|
||||
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'],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
You can create new functions in two ways:
|
||||
Dans votre gestionnaire, vous pouvez ensuite accéder à ces en-têtes :
|
||||
|
||||
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
|
||||
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
|
||||
```typescript
|
||||
const handler = async (event: RoutePayload) => {
|
||||
const signature = event.headers['x-webhook-signature'];
|
||||
const contentType = event.headers['content-type'];
|
||||
|
||||
### Generated typed client
|
||||
// Validate webhook signature...
|
||||
return { received: true };
|
||||
};
|
||||
```
|
||||
|
||||
Run yarn generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
|
||||
<Note>
|
||||
Les noms d'en-têtes sont normalisés en minuscules. Accédez-y en utilisant des clés en minuscules (par exemple, `event.headers['content-type']`).
|
||||
</Note>
|
||||
|
||||
Vous pouvez créer de nouvelles fonctions de deux façons :
|
||||
|
||||
* **Générée** : Exécutez `yarn app:create-entity` et choisissez l’option pour ajouter une nouvelle fonction. Cela génère un fichier de démarrage avec un gestionnaire et une configuration.
|
||||
* **Manuelle** : Créez un nouveau fichier `*.function.ts` et utilisez `defineFunction()`, en suivant le même modèle.
|
||||
|
||||
### Client typé généré
|
||||
|
||||
Exécutez yarn app:generate pour créer un client typé local dans generated/ basé sur le schéma de votre espace de travail. Utilisez-le dans vos fonctions :
|
||||
|
||||
```typescript
|
||||
import Twenty from './generated';
|
||||
@@ -466,34 +647,34 @@ const client = new Twenty();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
The client is re-generated by `yarn generate`. Re-run after changing your objects and `yarn sync` or when onboarding to a new workspace.
|
||||
Le client est régénéré par `yarn app:generate`. Relancez la commande après avoir modifié vos objets et exécuté `yarn app:sync`, ou lors de l’intégration à un nouvel espace de travail.
|
||||
|
||||
#### Runtime credentials in serverless functions
|
||||
#### Identifiants d’exécution dans les fonctions sans serveur
|
||||
|
||||
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
|
||||
Lorsque votre fonction s’exécute sur Twenty, la plateforme injecte des identifiants sous forme de variables d’environnement avant l’exécution de votre code :
|
||||
|
||||
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
|
||||
* `TWENTY_API_KEY`: Short‑lived key scoped to your application’s default function role.
|
||||
* `TWENTY_API_URL` : URL de base de l’API Twenty ciblée par votre application.
|
||||
* `TWENTY_API_KEY` : Clé de courte durée limitée au rôle de fonction par défaut de votre application.
|
||||
|
||||
Notes:
|
||||
|
||||
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
|
||||
* The API key’s permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
|
||||
* Applications can define roles to follow least‑privilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that role’s universal identifier.
|
||||
* Vous n’avez pas besoin de passer l’URL ou la clé API au client généré. Il lit `TWENTY_API_URL` et `TWENTY_API_KEY` depuis process.env à l’exécution.
|
||||
* Les autorisations de la clé API sont déterminées par le rôle référencé dans votre `application.config.ts` via `functionRoleUniversalIdentifier`. Il s’agit du rôle par défaut utilisé par les fonctions sans serveur de votre application.
|
||||
* Les applications peuvent définir des rôles pour appliquer le principe du moindre privilège. N’accordez que les autorisations dont vos fonctions ont besoin, puis faites pointer `functionRoleUniversalIdentifier` vers l’identifiant universel de ce rôle.
|
||||
|
||||
### Hello World example
|
||||
### Exemple Hello World
|
||||
|
||||
Explore a minimal, end-to-end example that demonstrates objects, functions, and multiple triggers [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world):
|
||||
Découvrez un exemple minimal de bout en bout qui démontre des objets, des fonctions et plusieurs déclencheurs [ici](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world) :
|
||||
|
||||
## Manual setup (without the scaffolder)
|
||||
## Configuration manuelle (sans l’outil de scaffolding)
|
||||
|
||||
While we recommend using `create-twenty-app` for the best getting-started experience, you can also set up a project manually. Do not install the CLI globally. Instead, add `twenty-sdk` as a local dependency and wire scripts in your package.json:
|
||||
Même si nous recommandons d’utiliser `create-twenty-app` pour une expérience de démarrage optimale, vous pouvez également configurer un projet manuellement. N’installez pas la CLI globalement. Ajoutez plutôt `twenty-sdk` comme dépendance locale et reliez des scripts dans votre package.json :
|
||||
|
||||
```bash filename="Terminal"
|
||||
yarn add -D twenty-sdk
|
||||
```
|
||||
|
||||
Then add scripts like these:
|
||||
Ajoutez ensuite des scripts comme ceux-ci :
|
||||
|
||||
```json filename="package.json"
|
||||
{
|
||||
@@ -510,13 +691,13 @@ Then add scripts like these:
|
||||
}
|
||||
```
|
||||
|
||||
Now you can run the same commands via Yarn, e.g. `yarn dev`, `yarn sync`, etc.
|
||||
Vous pouvez désormais exécuter les mêmes commandes via Yarn, par exemple `yarn app:dev`, `yarn app:sync`, etc.
|
||||
|
||||
## Troubleshooting
|
||||
## Résolution des problèmes
|
||||
|
||||
* Authentication errors: run `yarn auth` and ensure your API key has the required permissions.
|
||||
* Cannot connect to server: verify the API URL and that the Twenty server is reachable.
|
||||
* Types or client missing/outdated: run `yarn generate` and then `yarn dev`.
|
||||
* Dev mode not syncing: ensure `yarn dev` is running and that changes are not ignored by your environment.
|
||||
* Erreurs d’authentification : exécutez `yarn auth:login` et assurez-vous que votre clé API dispose des autorisations requises.
|
||||
* Impossible de se connecter au serveur : vérifiez l’URL de l’API et que le serveur Twenty est accessible.
|
||||
* Types ou client manquants/obsolètes : exécutez `yarn app:generate` puis `yarn app:dev`.
|
||||
* Le mode dev ne se synchronise pas : assurez-vous que `yarn app:dev` est en cours d’exécution et que les modifications ne sont pas ignorées par votre environnement.
|
||||
|
||||
Discord Help Channel: https://discord.com/channels/1130383047699738754/1130386664812982322
|
||||
Canal d’aide Discord : https://discord.com/channels/1130383047699738754/1130386664812982322
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
---
|
||||
title: Webhooks
|
||||
description: Receive real-time notifications when events occur in your CRM.
|
||||
description: Recevez des notifications en temps réel lorsque des événements se produisent dans votre CRM.
|
||||
---
|
||||
|
||||
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
|
||||
|
||||
Webhooks push data to your systems in real-time when events occur in Twenty — no polling required. Use them to keep external systems in sync, trigger automations, or send alerts.
|
||||
Les webhooks envoient des données à vos systèmes en temps réel lorsque des événements se produisent dans Twenty — aucune interrogation n'est nécessaire. Utilisez-les pour maintenir les systèmes externes synchronisés, déclencher des automatisations ou envoyer des alertes.
|
||||
|
||||
## Create a Webhook
|
||||
## Créer un Webhook
|
||||
|
||||
1. Go to **Settings → APIs & Webhooks → Webhooks**
|
||||
2. Click **+ Create webhook**
|
||||
3. Enter your webhook URL (must be publicly accessible)
|
||||
4. Click **Save**
|
||||
1. Allez dans **Paramètres → API & Webhooks → Webhooks**
|
||||
2. Cliquez sur **+ Créer un webhook**
|
||||
3. Saisissez votre URL de webhook (doit être publiquement accessible)
|
||||
4. Cliquez sur **Enregistrer**
|
||||
|
||||
The webhook activates immediately and starts sending notifications.
|
||||
Le webhook s'active immédiatement et commence à envoyer des notifications.
|
||||
|
||||
<VimeoEmbed videoId="928786708" title="Creating a webhook" />
|
||||
<VimeoEmbed videoId="928786708" title="Création d'un webhook" />
|
||||
|
||||
### Manage Webhooks
|
||||
### Gérer les Webhooks
|
||||
|
||||
**Edit**: Click the webhook → Update URL → **Save**
|
||||
**Modifier**: Cliquez sur le webhook → Mettre à jour l'URL → **Enregistrer**
|
||||
|
||||
**Delete**: Click the webhook → **Delete** → Confirm
|
||||
**Supprimer**: Cliquez sur le webhook → **Supprimer** → Confirmer
|
||||
|
||||
## Events
|
||||
## Événements
|
||||
|
||||
Twenty sends webhooks for these event types:
|
||||
Twenty envoie des webhooks pour ces types d'événements :
|
||||
|
||||
| Event | Example |
|
||||
| ------------------ | ---------------------------------------------------------- |
|
||||
| **Record Created** | `person.created`, `company.created`, `note.created` |
|
||||
| **Record Updated** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **Record Deleted** | `person.deleted`, `company.deleted` |
|
||||
| Événement | Exemple |
|
||||
| ----------------------------- | ---------------------------------------------------------- |
|
||||
| **Enregistrement créé** | `person.created`, `company.created`, `note.created` |
|
||||
| **Enregistrement mis à jour** | `person.updated`, `company.updated`, `opportunity.updated` |
|
||||
| **Enregistrement supprimé** | `person.deleted`, `company.deleted` |
|
||||
|
||||
All event types are sent to your webhook URL. Event filtering may be added in future releases.
|
||||
Tous les types d'événements sont envoyés à votre URL de webhook. Le filtrage des événements pourrait être ajouté dans de prochaines versions.
|
||||
|
||||
## Payload Format
|
||||
## Format de la charge utile
|
||||
|
||||
Each webhook sends an HTTP POST with a JSON body:
|
||||
Chaque webhook envoie une requête HTTP POST avec un corps JSON :
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -55,35 +55,35 @@ Each webhook sends an HTTP POST with a JSON body:
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| ----------- | ------------------------------------------------ |
|
||||
| `event` | What happened (e.g., `person.created`) |
|
||||
| `data` | The full record that was created/updated/deleted |
|
||||
| `timestamp` | When the event occurred (UTC) |
|
||||
| Champ | Description |
|
||||
| ------------ | ----------------------------------------------------------- |
|
||||
| `événement` | Ce qui s'est passé (par exemple, `person.created`) |
|
||||
| `données` | L'enregistrement complet qui a été créé/mis à jour/supprimé |
|
||||
| `horodatage` | Quand l'événement s'est produit (UTC) |
|
||||
|
||||
<Note>
|
||||
Respond with a **2xx HTTP status** (200-299) to acknowledge receipt. Non-2xx responses are logged as delivery failures.
|
||||
Répondez avec un **statut HTTP 2xx** (200-299) pour accuser réception. Les réponses non 2xx sont consignées comme des échecs de livraison.
|
||||
</Note>
|
||||
|
||||
## Webhook Validation
|
||||
## Validation des Webhooks
|
||||
|
||||
Twenty signs each webhook request for security. Validate signatures to ensure requests are authentic.
|
||||
Twenty signe chaque requête de webhook pour des raisons de sécurité. Validez les signatures pour garantir l'authenticité des requêtes.
|
||||
|
||||
### Headers
|
||||
### En-têtes
|
||||
|
||||
| Header | Description |
|
||||
| ---------------------------- | --------------------- |
|
||||
| `X-Twenty-Webhook-Signature` | HMAC SHA256 signature |
|
||||
| `X-Twenty-Webhook-Timestamp` | Request timestamp |
|
||||
| En-tête | Description |
|
||||
| ---------------------------- | ------------------------ |
|
||||
| `X-Twenty-Webhook-Signature` | Signature HMAC SHA256 |
|
||||
| `X-Twenty-Webhook-Timestamp` | Horodatage de la requête |
|
||||
|
||||
### Validation Steps
|
||||
### Étapes de validation
|
||||
|
||||
1. Get the timestamp from `X-Twenty-Webhook-Timestamp`
|
||||
2. Create the string: `{timestamp}:{JSON payload}`
|
||||
3. Compute HMAC SHA256 using your webhook secret
|
||||
4. Compare with `X-Twenty-Webhook-Signature`
|
||||
1. Récupérez l'horodatage depuis `X-Twenty-Webhook-Timestamp`
|
||||
2. Créez la chaîne : `{timestamp}:{JSON payload}`
|
||||
3. Calculez le HMAC SHA256 en utilisant votre secret de webhook
|
||||
4. Comparez avec `X-Twenty-Webhook-Signature`
|
||||
|
||||
### Example (Node.js)
|
||||
### Exemple (Node.js)
|
||||
|
||||
```javascript
|
||||
const crypto = require("crypto");
|
||||
@@ -103,10 +103,10 @@ const isValid = expectedSignature === req.headers["x-twenty-webhook-signature"];
|
||||
|
||||
## Webhooks vs Workflows
|
||||
|
||||
| Method | Direction | Use Case |
|
||||
| ---------------------------- | --------- | ---------------------------------------------------------- |
|
||||
| **Webhooks** | OUT | Automatically notify external systems of any record change |
|
||||
| **Workflow + HTTP Request** | OUT | Send data out with custom logic (filters, transformations) |
|
||||
| **Workflow Webhook Trigger** | IN | Receive data into Twenty from external systems |
|
||||
| Méthode | Direction | Cas d'utilisation |
|
||||
| -------------------------------------- | --------- | ------------------------------------------------------------------------------------- |
|
||||
| **Webhooks** | SORTIE | Notifier automatiquement les systèmes externes de toute modification d'enregistrement |
|
||||
| **Workflow + requête HTTP** | SORTIE | Envoyer des données avec une logique personnalisée (filtres, transformations) |
|
||||
| **Déclencheur de webhook de workflow** | ENTRÉE | Recevoir des données dans Twenty depuis des systèmes externes |
|
||||
|
||||
For receiving external data, see [Set Up a Webhook Trigger](/l/fr/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
|
||||
Pour recevoir des données externes, voir [Configurer un déclencheur de webhook](/l/fr/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
---
|
||||
title: Extend
|
||||
description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
|
||||
title: Étendre
|
||||
description: Étendez les fonctionnalités de Twenty avec des API, des webhooks et des applications personnalisées.
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/user-guide/integrations/plug.png" alt="AI" />
|
||||
<img src="/images/user-guide/integrations/plug.png" alt="IA" />
|
||||
</Frame>
|
||||
|
||||
## Overview
|
||||
## Vue d'ensemble
|
||||
|
||||
Twenty is designed to be extensible. Use our APIs, webhooks, and app framework to integrate with your existing tools and build custom functionality.
|
||||
Twenty est conçu pour être extensible. Utilisez nos API, nos webhooks et notre framework d'applications pour vous intégrer à vos outils existants et créer des fonctionnalités personnalisées.
|
||||
|
||||
## What You Can Do
|
||||
## Ce que vous pouvez faire
|
||||
|
||||
* **APIs**: Query and modify your CRM data programmatically using REST or GraphQL
|
||||
* **Webhooks**: Receive real-time notifications when events occur in Twenty
|
||||
* **Apps**: Build custom applications that extend Twenty's capabilities - Coming soon!
|
||||
* **API** : Interrogez et modifiez vos données CRM par programmation à l'aide de REST ou de GraphQL
|
||||
* **Webhooks** : Recevez des notifications en temps réel lorsque des événements se produisent dans Twenty
|
||||
* **Applications** : Créez des applications personnalisées qui étendent les capacités de Twenty - Bientôt disponible !
|
||||
|
||||
## Getting Started
|
||||
## Prise en main
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="APIs" icon="code" href="/l/fr/developers/extend/capabilities/apis">
|
||||
Connect to Twenty programmatically
|
||||
<Card title="API" icon="code" href="/l/fr/developers/extend/capabilities/apis">
|
||||
Connectez-vous à Twenty par programmation
|
||||
</Card>
|
||||
|
||||
<Card title="Webhooks" icon="bell" href="/l/fr/developers/extend/capabilities/webhooks">
|
||||
Get notified of events in real-time
|
||||
Recevez des notifications d'événements en temps réel
|
||||
</Card>
|
||||
|
||||
<Card title="Apps" icon="puzzle-piece" href="/l/fr/developers/extend/capabilities/apps">
|
||||
Build customizations as code (Alpha)
|
||||
<Card title="Applications" icon="puzzle-piece" href="/l/fr/developers/extend/capabilities/apps">
|
||||
Créez des personnalisations sous forme de code (Alpha)
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Reference in New Issue
Block a user