snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
---
title: Gérer les tableaux dans les actions Code
description: Apprenez à gérer correctement les entrées de type tableau dans les actions Code des workflows.
---
Lorsque vous travaillez avec des tableaux dans les actions Code, vous pouvez rencontrer deux difficultés courantes :
1. **Tableaux passés sous forme de chaînes** — les données provenant de systèmes externes ou d'étapes précédentes arrivent sous forme de chaîne plutôt que sous forme de véritable tableau
2. **Impossible de sélectionner des éléments individuellement** — vous ne pouvez sélectionner que le tableau entier, pas des champs spécifiques à l'intérieur
Les deux peuvent être résolus avec un nœud Code.
## Analyse des tableaux à partir de chaînes
Les tableaux sont souvent transmis entre les étapes d'un workflow sous forme de chaînes ou de JSON plutôt que comme tableaux natifs. Cela se produit lorsque :
* Réception de données depuis des API externes via HTTP Request
* Traitement des charges utiles des webhooks
* Transmission de données entre les étapes du workflow
**Solution** : Ajoutez ce modèle au début de votre action Code :
```javascript
export const main = async (params: {
users: any;
}): Promise<object> => {
const { users } = params;
// Handle input that may come as a string or an array
const usersFormatted = typeof users === "string" ? JSON.parse(users) : users;
// Now you can safely work with usersFormatted as an array
return {
users: usersFormatted.map((user) => ({
...user,
activityStatus: String(user.activityStatus).toUpperCase(),
})),
};
};
```
La ligne clé `typeof users === "string" ? JSON.parse(users) : users` vérifie si l'entrée est une chaîne, l'analyse si nécessaire, ou l'utilise directement si c'est déjà un tableau.
## Extraction de champs individuels à partir de tableaux
Un webhook peut renvoyer un tableau comme `answers: [...]`, mais dans les étapes de workflow suivantes vous ne pouvez sélectionner que le tableau entier — pas des éléments individuels à l'intérieur.
**Solution** : Ajoutez un nœud Code pour extraire des champs spécifiques et les renvoyer sous forme d'objet structuré :
```javascript
export const main = async (params: {
answers: any;
}): Promise<object> => {
const { answers } = params;
// Handle input that may come as a string or an array
const answersFormatted = typeof answers === "string"
? JSON.parse(answers)
: answers;
// Extract specific fields from the array
const firstname = answersFormatted[0]?.text || "";
const name = answersFormatted[1]?.text || "";
return {
answer: {
firstname,
name
}
};
};
```
Le nœud Code renvoie un objet structuré au lieu d'un tableau. Dans les étapes suivantes, vous pouvez maintenant sélectionner des champs individuels comme `answer.firstname` et `answer.name` à partir du sélecteur de variables.
<Tip>
**Vous voulez parcourir le tableau au lieu d'extraire des champs ?** Lorsqu'une étape de fonction Code ou Logic renvoie un tableau de premier niveau, vous pouvez l'envoyer directement dans un [Iterator](/l/fr/user-guide/workflows/capabilities/use-iterator) : sélectionnez l'option **Whole list** de l'étape comme entrée de l'Iterator, puis référencez chaque élément avec `{{iterator.currentItem}}` à l'intérieur de la boucle. Dans ce cas, vous n'avez pas besoin de restructurer le tableau en un objet.
</Tip>
<Tip>
Cliquez sur l'icône carrée en haut à droite de l'éditeur de code pour l'afficher en plein écran — utile car la largeur par défaut de l'éditeur est limitée.
</Tip>

View File

@@ -0,0 +1,182 @@
---
title: Importer les données de produits dans Twenty
description: Synchronisez les données du catalogue de produits depuis un entrepôt de données vers votre CRM selon une planification.
---
Utilisez ce modèle pour garder Twenty synchronisé avec les données produits de votre entrepôt de données (par ex. Snowflake, BigQuery, PostgreSQL).
## Structure du flux de travail
1. **Déclencheur** : Selon une planification
2. **Code** : Interroger votre entrepôt de données
3. **Code** (facultatif) : Formater les données en tableau
4. **Iterator** : Parcourir chaque produit
5. **Upsert Record** : Créer ou mettre à jour dans Twenty
<img src="/images/user-guide/workflows/create_users_workflow.png" style={{width:'100%'}} />
## Étape 1 : Planifier le déclencheur
Définissez le flux de travail pour s'exécuter à une fréquence correspondant à vos besoins d'actualisation des données :
* Toutes les 5 minutes pour une synchronisation quasi en temps réel
* Toutes les heures pour des données moins critiques
* Quotidiennement pour des mises à jour par lots
## Étape 2 : Interroger votre entrepôt de données
Ajoutez une action **Code** pour récupérer les données récentes :
```javascript
export const main = async () => {
const intervalMinutes = 10; // Match your schedule frequency
const cutoffTime = new Date(Date.now() - intervalMinutes * 60 * 1000).toISOString();
// Replace with your actual data warehouse connection
const response = await fetch("https://your-warehouse-api.com/query", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
query: `
SELECT id, name, sku, price, stock_quantity, updated_at
FROM products
WHERE updated_at >= '${cutoffTime}'
`
})
});
const data = await response.json();
return { products: data.results };
};
```
<Tip>
Filtrez par `updated_at >= last X minutes` pour ne récupérer que les enregistrements récemment modifiés. Cela rend la synchronisation plus efficace.
</Tip>
## Étape 3 : Formater les données (facultatif)
Si votre entrepôt renvoie des données dans un format nécessitant une transformation, ajoutez une autre action **Code**. Les transformations courantes incluent les conversions de type, le renommage de champs et le nettoyage des données.
### Exemple : données utilisateur avec champs booléens et de statut
```javascript
export const main = async (params: {
users: any;
}): Promise<object> => {
const { users } = params;
const usersFormatted = typeof users === "string" ? JSON.parse(users) : users;
// Convert string "true"/"false" to actual booleans
const toBool = (v: any) => v === true || v === "true";
return {
users: usersFormatted.map((user) => ({
...user,
activityStatus: String(user.activityStatus).toUpperCase(),
isActiveLast30d: toBool(user.isActiveLast30d),
isActiveLast7d: toBool(user.isActiveLast7d),
isActiveLast24h: toBool(user.isActiveLast24h),
isTwenty: toBool(user.isTwenty),
})),
};
};
```
### Exemple : données produit avec conversions de type
```javascript
export const main = async (params: { products: any }) => {
const products = typeof params.products === "string"
? JSON.parse(params.products)
: params.products;
return {
products: products.map(product => ({
externalId: product.id,
name: product.name,
sku: product.sku,
price: parseFloat(product.price), // String → Number
stockQuantity: parseInt(product.stock_quantity),
isActive: product.status === "active" // String → Boolean
}))
};
};
```
### Exemple : formatage des dates et des devises
```javascript
export const main = async (params: { deals: any }) => {
const deals = typeof params.deals === "string"
? JSON.parse(params.deals)
: params.deals;
return {
deals: deals.map(deal => ({
...deal,
// Convert Unix timestamp to ISO date
closedAt: deal.closed_timestamp
? new Date(deal.closed_timestamp * 1000).toISOString()
: null,
// Ensure amount is a number (remove currency symbols)
amount: parseFloat(String(deal.amount).replace(/[^0-9.-]/g, "")),
// Normalize stage names
stage: deal.stage?.toLowerCase().replace(/_/g, " ")
}))
};
};
```
### Transformations courantes
| Format source | Format cible | Code |
| -------------------- | ---------------- | ---------------------------------------- |
| `"true"` / `"false"` | `true` / `false` | `v === true \|\| v === "true"` |
| `"123.45"` | `123.45` | `parseFloat(value)` |
| `"active"` | `"ACTIVE"` | `value.toUpperCase()` |
| `1704067200` (Unix) | Date ISO | `new Date(v * 1000).toISOString()` |
| `"$1,234.56"` | `1234.56` | `parseFloat(v.replace(/[^0-9.-]/g, ""))` |
| `null` / `undefined` | `""` | `value \|\| ""` |
## Étape 4 : Itérer sur les produits
Ajoutez une action **Itérateur** :
* Entrée : `{{code.products}}`
Cela parcourt chaque produit du tableau.
## Étape 5 : Effectuer un upsert pour chaque enregistrement
À l'intérieur de l'itérateur, ajoutez une action **Upsert Record** :
| Paramètre | Valeur |
| ---------------------- | -------------------------------------- |
| **Objet** | Votre objet Produit personnalisé |
| **Correspondance par** | ID externe ou SKU (identifiant unique) |
| **Nom** | `{{iterator.item.name}}` |
| **SKU** | `{{iterator.item.sku}}` |
| **Prix** | `{{iterator.item.price}}` |
<Tip>
Utilisez **Upsert** (mettre à jour ou créer) au lieu de construire des branches distinctes pour la création et la mise à jour. C'est plus rapide à construire et plus facile à déboguer.
</Tip>
## Exemples de cas d'utilisation
| Source | Données |
| --------------------------------- | ------------------------------------------------ |
| **Système ERP** | Catalogue de produits, tarification, stocks |
| **Plateforme e-commerce** | Commandes, clients, mises à jour des produits |
| **Entrepôt de données** | Indicateurs agrégés, données enrichies |
| **Système de gestion des stocks** | Niveaux de stock, alertes de réapprovisionnement |
## Articles connexes
* [Déclencheurs de workflows](/l/fr/user-guide/workflows/capabilities/workflow-triggers)
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Gérer les tableaux dans les actions Code](/l/fr/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,130 @@
---
title: Importer les réponses de Typeform dans Twenty
description: Gérez la charge utile du webhook de Typeform pour créer des prospects à partir des réponses au formulaire.
---
Pour la configuration standard d'un webhook, voir [Configurer un déclencheur de webhook](/l/fr/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger). Cet article couvre le traitement spécifique nécessaire pour la structure de charge utile personnalisée de Typeform.
### Étape 1 : Créer un flux de travail de webhook
1. Allez dans **Paramètres → Flux de travail**
2. Cliquez sur **+ Nouveau flux de travail**
3. Sélectionnez **Webhook** comme déclencheur
4. Copiez l'URL du webhook
### Étape 2 : Configurer Typeform
1. Dans Typeform, ouvrez votre formulaire
2. Allez dans **Connect → Webhooks**
3. Collez l'URL du webhook Twenty
4. Enregistrer
### Étape 3 : Comprendre la charge utile de Typeform
Typeform envoie une structure JSON imbriquée. Voici un exemple simplifié :
```json
{
"event_type": "form_response",
"form_response": {
"form_id": "abc123",
"submitted_at": "2025-01-15T10:30:00Z",
"answers": [
{
"text": "Jane",
"type": "text",
"field": { "id": "field1", "type": "short_text", "title": "First Name" }
},
{
"text": "Smith",
"type": "text",
"field": { "id": "field2", "type": "short_text", "title": "Last Name" }
},
{
"text": "Acme Corp",
"type": "text",
"field": { "id": "field3", "type": "short_text", "title": "Company" }
},
{
"email": "jane@acme.com",
"type": "email",
"field": { "id": "field4", "type": "email", "title": "Email" }
},
{
"type": "choice",
"field": { "id": "field5", "type": "dropdown", "title": "Team Size" },
"choice": { "label": "10-50" }
}
]
}
}
```
Points clés à noter :
* Les données du formulaire sont imbriquées sous `form_response`
* **Les réponses sont renvoyées sous forme de tableau**, et non comme des champs nommés
* Chaque réponse inclut le type et le titre du champ à titre de référence
### Étape 4 : Extraire les champs du tableau des réponses
Comme `answers` est un tableau, vous ne pouvez sélectionner que le tableau entier dans les étapes suivantes — pas des champs individuels. Ajoutez une action **Code** pour extraire les champs dont vous avez besoin :
```javascript
export const main = async (params: {
answers: any;
}): Promise<object> => {
const { answers } = params;
// Handle input that may come as a string or an array
const answersFormatted = typeof answers === "string"
? JSON.parse(answers)
: answers;
// Extract fields by position or by finding the field type
const firstName = answersFormatted[0]?.text || "";
const lastName = answersFormatted[1]?.text || "";
const company = answersFormatted[2]?.text || "";
const email = answersFormatted.find(a => a.type === "email")?.email || "";
const teamSize = answersFormatted.find(a => a.type === "choice")?.choice?.label || "";
return {
contact: {
firstName,
lastName,
company,
email,
teamSize
}
};
};
```
Dans les étapes suivantes, vous pouvez sélectionner `contact.firstName`, `contact.email`, etc. à partir du sélecteur de variables.
<Tip>
Pour plus de détails sur la gestion des tableaux dans les actions Code, voir [Gérer les tableaux dans les actions Code](/l/fr/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions).
</Tip>
### Étape 5 : Créer l'enregistrement
Ajoutez une action **Créer un enregistrement** :
| Champ | Valeur |
| ------------------ | ------------------------------------------------------------- |
| **Objet** | Personnes |
| **Prénom** | `{{code.contact.firstName}}` |
| **Nom de famille** | `{{code.contact.lastName}}` |
| **E-mail** | `{{code.contact.email}}` |
| **Entreprise** | Rechercher ou créer en fonction de `{{code.contact.company}}` |
### Étape 6 : Tester et activer
1. Soumettez une réponse de test dans Typeform
2. Vérifiez l'exécution du flux de travail pour confirmer que les données ont été capturées
3. Activez le flux de travail
## Articles connexes
* [Configurer un déclencheur de webhook](/l/fr/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger)
* [Gérer les tableaux dans les actions Code](/l/fr/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,263 @@
---
title: Générer un PDF depuis Twenty
description: Créez un flux de travail pour générer et joindre un PDF (par exemple un devis) à un enregistrement.
---
Générez ou récupérez automatiquement un PDF et joignez-le à un enregistrement dans Twenty. Cette fonctionnalité est couramment utilisée pour créer des devis, des factures ou des rapports liés aux Entreprises, aux Opportunités ou à d'autres objets.
## Vue d'ensemble
Ce flux de travail utilise un **Déclencheur manuel** afin que les utilisateurs puissent générer un PDF à la demande pour tout enregistrement sélectionné. Une **fonction logique** gère :
1. Téléchargement du PDF depuis une URL (provenant d'un service de génération de PDF)
2. Téléversement du fichier dans Twenty
3. Création d'une pièce jointe liée à l'enregistrement
## Prérequis
Avant de configurer le flux de travail :
1. **Créer une clé API** : Allez dans **Settings → APIs** et créez une nouvelle clé API. Vous aurez besoin de ce jeton pour la fonction logique.
2. **Configurer un service de génération de PDF** (facultatif) : Si vous souhaitez générer des PDF dynamiquement (par exemple, des devis), utilisez un service comme Carbone, PDFMonkey ou DocuSeal pour créer le PDF et obtenir une URL de téléchargement.
## Configuration pas à pas
### Étape 1 : Configurer le déclencheur
1. Allez dans **Flux de travail** et créez un nouveau flux de travail
2. Sélectionnez **Déclencheur manuel**
3. Choisissez l'objet auquel vous souhaitez joindre des PDF (par exemple, **Entreprise** ou **Opportunité**)
<Tip>
Avec un déclencheur manuel, les utilisateurs peuvent exécuter ce workflow en utilisant un bouton qui apparaît en haut à droite une fois qu'un enregistrement est sélectionné — cela exécute le workflow pour générer et joindre un PDF.
</Tip>
### Étape 2 : Ajouter une fonction logique
1. Ajoute une action **Code** (fonction logique)
2. Créez une nouvelle fonction avec le code ci-dessous
3. Configurez les paramètres d'entrée
#### Paramètres d'entrée
| Paramètre | Valeur |
| ----------- | ----------------------- |
| `companyId` | `{{trigger.object.id}}` |
<Note>
Si vous l'attachez à un objet différent (Personne, Opportunité, etc.), renommez le paramètre en conséquence (par exemple, `personId`, `opportunityId`) et mettez à jour la fonction logique.
</Note>
#### Code de la fonction logique
```typescript
export const main = async (
params: { companyId: string },
) => {
const { companyId } = params;
// Replace with your Twenty GraphQL endpoints (/metadata for metadata and files or /graphql for your records)
// Cloud: https://api.twenty.com/graphql
// Self-hosted: https://your-domain.com/graphql
const metadataGraphqlEndpoint = 'https://api.twenty.com/metadata';
const dataGraphqlEndpoint = 'https://api.twenty.com/graphql';
// Replace with your API key from Settings → APIs
const authToken = 'YOUR_API_KEY';
// Replace with your PDF URL
// This could be from a PDF generation service or a static URL
const pdfUrl = 'https://your-pdf-service.com/generated-quote.pdf';
const filename = 'quote.pdf';
// Step 1: Download the PDF file
const pdfResponse = await fetch(pdfUrl);
if (!pdfResponse.ok) {
throw new Error(`Failed to download PDF: ${pdfResponse.status}`);
}
const pdfBlob = await pdfResponse.blob();
const pdfFile = new File([pdfBlob], filename, { type: 'application/pdf' });
const fieldMetadataIdQuery = `
query FindUploadFileFieldMetadataId {
objects {
edges {
node {
nameSingular
fieldsList {
id
name
}
}
}
}
}
`;
// Step 2: Find a fieldMetadataId of "Attachment file" field in Attachments object with GraphQL API
const response = await fetch(metadataGraphqlEndpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`
},
body: {
query: fieldMetadataIdQuery,
}
});
const result = await response.json();
const uploadFileFieldMetadataId = result.data.objects.edges.find(object => object.node.nameSingular === 'attachment').node.fieldsList.find(field => field.name === 'file').id;
// Step 3: Upload the file via GraphQL multipart upload
const uploadMutation = `
mutation UploadFilesFieldFile($file: Upload!, $fieldMetadataId: String!) {
uploadFilesFieldFile(file: $file, fieldMetadataId: $fieldMetadataId) {
id
}
}
`;
const uploadForm = new FormData();
uploadForm.append('operations', JSON.stringify({
query: uploadMutation,
variables: { file: null, fieldMetadataId: uploadFileFieldMetadataId },
}));
uploadForm.append('map', JSON.stringify({ '0': ['variables.file'] }));
uploadForm.append('0', pdfFile);
const uploadResponse = await fetch(metadataGraphqlEndpoint, {
method: 'POST',
headers: { Authorization: `Bearer ${authToken}` },
body: uploadForm,
});
const uploadResult = await uploadResponse.json();
if (uploadResult.errors?.length) {
throw new Error(`Upload failed: ${uploadResult.errors[0].message}`);
}
const fileId = uploadResult.data?.uploadFilesFieldFile?.id;
if (!fileId) {
throw new Error('No file id returned from upload');
}
// Step 4: Create the attachment linked to the company
const attachmentMutation = `
mutation CreateOneAttachment($data: AttachmentCreateInput!) {
createAttachment(data: $data) {
id
name
}
}
`;
const attachmentResponse = await fetch(dataGraphqlEndpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: attachmentMutation,
variables: {
data: {
name: filename,
targetCompanyId: companyId,
file: [
{
fileId: fileId,
label: filename
}
]
},
},
}),
});
const attachmentResult = await attachmentResponse.json();
if (attachmentResult.errors?.length) {
throw new Error(`Attachment creation failed: ${attachmentResult.errors[0].message}`);
}
return attachmentResult.data?.createAttachment;
};
```
### Étape 3 : Personnaliser selon votre cas d'utilisation
#### Pour joindre à un objet différent
Remplacez `targetCompanyId` par le champ approprié :
| Objet | Nom du champ |
| ------------------ | -------------------------- |
| Entreprise | `targetCompanyId` |
| Personne | `targetPersonId` |
| Opportunité | `targetOpportunityId` |
| Objet personnalisé | `targetYourCustomObjectId` |
Mettez à jour à la fois le paramètre de la fonction et l'objet `variables.data` dans la mutation de pièce jointe.
#### Pour utiliser une URL de PDF dynamique
Si vous utilisez un service de génération de PDF, vous pouvez :
1. Commencez par créer une action HTTP Request pour générer le PDF
2. Transmettez l'URL du PDF retournée à la fonction logique en tant que paramètre
```typescript
export const main = async (
params: { companyId: string; pdfUrl: string; filename: string },
) => {
const { companyId, pdfUrl, filename } = params;
// ... rest of the function
};
```
### Étape 4 : Tester et activer
1. Enregistrez le flux de travail
2. Accédez à un enregistrement d'Entreprise
3. Cliquez sur le menu **⋮** et sélectionnez votre flux de travail
4. Vérifiez la section **Pièces jointes** de l'enregistrement pour confirmer que le PDF a été joint
5. Activez le flux de travail
## Combiner avec des services de génération de PDF
Pour créer des devis ou des factures dynamiques :
### Exemple : Générer un devis → Joindre le PDF
| Étape | Action | Objectif |
| ----- | ------------------------------- | ----------------------------------------------------------------------- |
| 1 | Déclencheur manuel (Entreprise) | L'utilisateur lance l'action depuis un enregistrement |
| 2 | Rechercher un enregistrement | Obtenir les détails de l'Opportunité ou des lignes d'articles |
| 3 | Requête HTTP | Appeler l'API de génération de PDF avec les données de l'enregistrement |
| 4 | Fonction logique | Télécharger et joindre le PDF généré |
### Services populaires de génération de PDF
* **Carbone** - Génération de documents basée sur des modèles
* **PDFMonkey** - Création de PDF dynamiques à partir de modèles
* **DocuSeal** - Plateforme d'automatisation de documents
* **Documint** - Génération de documents axée sur l'API
Chaque service fournit une API qui retourne une URL PDF, que vous pouvez ensuite passer à la fonction logique.
## Résolution des problèmes
| Problème | Solution |
| ----------------------------------------- | ----------------------------------------------------------------------------- |
| "Échec du téléchargement du PDF" | Vérifiez que l'URL du PDF est accessible et renvoie un PDF valide |
| "Échec du téléversement" | Vérifiez que votre clé API est valide et dispose des autorisations d'écriture |
| "Échec de la création de la pièce jointe" | Assurez-vous que le nom du champ d'ID d'objet correspond à votre objet cible |
## Articles connexes
* [Déclencheurs de workflows](/l/fr/user-guide/workflows/capabilities/workflow-triggers)
* [Fonctions logiques](/l/fr/user-guide/workflows/capabilities/workflow-actions#code)
* [Générer un devis ou une facture depuis Twenty](/l/fr/user-guide/workflows/how-tos/connect-to-other-tools/generate-quote-or-invoice-from-twenty)

View File

@@ -0,0 +1,144 @@
---
title: Générer un devis ou une facture depuis Twenty
description: Créer automatiquement des factures dans des outils externes lorsque des affaires se clôturent.
---
Envoyez automatiquement les données de l'affaire vers votre système de facturation (Stripe, QuickBooks, Xero, etc.) lorsqu'une opportunité est gagnée.
## Structure du flux de travail
1. **Déclencheur** : L'enregistrement est mis à jour (Opportunité)
2. **Filtre** : Étape = Closed Won
3. **Rechercher un enregistrement** : Obtenir les détails de l'Entreprise
4. **Code** (facultatif) : Mettre en forme la charge utile
5. **Requête HTTP** : Envoyer au système de facturation
## Étape 1 : Configurer le déclencheur
1. Créer un nouveau workflow
2. Sélectionnez le déclencheur **L'enregistrement est mis à jour**
3. Choisissez **Opportunité** comme objet
## Étape 2 : Filtrer pour Closed Won
Ajoutez une action **Filtre** pour ne continuer que lorsque l'affaire est gagnée :
| Paramètre | Valeur |
| ------------- | --------------------------------------- |
| **Champ** | Étape |
| **Condition** | Égal à |
| **Valeur** | `CLOSED_WON` (ou le nom de votre étape) |
<Tip>
Le déclencheur se déclenche lors de toute mise à jour de l'Opportunité. Le Filtre garantit que le flux de travail ne continue que lorsque l'étape passe à Closed Won.
</Tip>
## Étape 3 : Obtenir les détails de l'Entreprise
L'enregistrement de l'Opportunité peut ne pas inclure tous les champs de l'Entreprise dont vous avez besoin pour la facture. Ajoutez une action **Rechercher un enregistrement** :
| Paramètre | Valeur |
| ---------------------- | ---------------------------------------- |
| **Objet** | Entreprise |
| **Correspondance par** | ID égal à `{{trigger.object.companyId}}` |
Cela récupère l'enregistrement complet de l'Entreprise avec l'adresse de facturation, l'ID fiscal, etc.
## Étape 4 : Formater la charge utile (facultatif)
Si votre système de facturation attend un format spécifique, ajoutez une action **Code** :
```javascript
export const main = async (params: {
opportunity: any;
company: any;
}): Promise<object> => {
const { opportunity, company } = params;
return {
invoice: {
// Customer info from Company
customer_name: company.name,
customer_email: company.email || "",
billing_address: {
line1: company.address?.street || "",
city: company.address?.city || "",
postal_code: company.address?.postalCode || "",
country: company.address?.country || ""
},
tax_id: company.taxId || null,
// Invoice details from Opportunity
amount: opportunity.amount,
currency: opportunity.currency || "USD",
description: `Invoice for ${opportunity.name}`,
due_days: 30,
// Reference back to Twenty
metadata: {
opportunity_id: opportunity.id,
company_id: company.id
}
}
};
};
```
## Étape 5 : Envoyer au système de facturation
Ajoutez une action **Requête HTTP** :
| Paramètre | Valeur |
| ------------ | ---------------------------------------------------------------- |
| **Méthode** | POST |
| **URL** | Votre point de terminaison API de facturation |
| **En-têtes** | `Authorization: Bearer YOUR_API_KEY` |
| **Corps** | `{{code.invoice}}` ou faites correspondre les champs directement |
### Exemple : Facture Stripe
```
POST https://api.stripe.com/v1/invoices
Headers:
Authorization: Bearer sk_live_xxx
Content-Type: application/x-www-form-urlencoded
Body:
customer: {{company.stripeCustomerId}}
collection_method: send_invoice
days_until_due: 30
```
### Exemple : Facture QuickBooks
```
POST https://quickbooks.api.intuit.com/v3/company/{realmId}/invoice
Headers:
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Body: {{code.invoice}}
```
## Récapitulatif complet du flux de travail
| Étape | Action | Objectif |
| ----- | --------------------------------------- | ------------------------------------------------------------ |
| 1 | Déclencheur : Enregistrement mis à jour | Se déclenche à chaque modification d'une Opportunité |
| 2 | Filtre | Ne poursuivre que si Étape = Closed Won |
| 3 | Rechercher un enregistrement | Obtenir tous les détails de l'Entreprise pour la facturation |
| 4 | Code | Mettre en forme les données pour l'API de facturation |
| 5 | Requête HTTP | Créer la facture dans le système externe |
## Conseils
* **Stockez les ID externes** : Enregistrez l'ID de la facture renvoyé par l'API dans l'Opportunité à l'aide d'une action **Mettre à jour l'enregistrement**
* **Gestion des erreurs** : Ajoutez une branche pour envoyer une notification si la requête HTTP échoue
* **Testez d'abord** : Utilisez le mode bac à sable/test de votre système de facturation avant la mise en production
## Articles connexes
* [Générer un PDF à partir de Twenty](/l/fr/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty) — joindre les PDF générés aux enregistrements
* [Déclencheurs de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-triggers)
* [Actions de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Automatisations Closed Won](/l/fr/user-guide/workflows/how-tos/crm-automations/closed-won-automations)

View File

@@ -0,0 +1,170 @@
---
title: Configurer un déclencheur de webhook
description: Recevez des données de services externes pour déclencher des flux de travail.
---
Les déclencheurs de webhook permettent à des services externes de démarrer vos flux de travail en envoyant des données vers une URL unique. Utilisez-les pour connecter des formulaires, des applications tierces et des intégrations personnalisées.
## Quand utiliser les webhooks
| Cas d'utilisation | Exemple |
| ------------------------------- | -------------------------------------------------------------- |
| **Formulaires web** | Les soumissions de formulaires de contact créent des prospects |
| **Applications tierces** | Paiement Stripe → création d'un enregistrement client |
| **Intégrations personnalisées** | Votre application → automatisation dans Twenty |
| **Outils sans code** | Connexions Zapier, Make, n8n |
## Configuration pas à pas
### Étape 1 : Créer le flux de travail
1. Allez dans **Paramètres → Flux de travail**
2. Cliquez sur **+ Nouveau flux de travail**
3. Nommez-le (par exemple, "Soumission de formulaire de site web")
### Étape 2 : Configurer le déclencheur de webhook
1. Cliquez sur le bloc de déclenchement
2. Sélectionnez **Webhook**
3. Vous recevrez une URL de webhook unique, par exemple :
```
https://api.twenty.com/webhooks/workflow/abc123...
```
4. Copiez cette URL — vous en aurez besoin pour votre service externe
### Étape 3 : Définir la structure de données attendue
Pour les requêtes **POST**, définissez la structure du corps attendue :
1. Cliquez sur **Définir le corps attendu**
2. Saisissez un JSON d'exemple correspondant à ce que votre service enverra :
```json
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"company": "Acme Inc",
"message": "Interested in your product"
}
```
3. Cliquez sur **Enregistrer** — cela crée des variables que vous pouvez utiliser dans les étapes suivantes
### Étape 4 : Ajouter des actions
Ajoutez maintenant des actions qui utilisent les données du webhook :
**Exemple : Créer un enregistrement de personne**
1. Ajoutez l'action **Créer un enregistrement**
2. Sélectionnez l'objet **People**
3. Associez les champs :
| Champ | Valeur |
| -------------- | ------------------------------------------------------------- |
| Prénom | `{{trigger.body.firstName}}` |
| Nom de famille | `{{trigger.body.lastName}}` |
| E-mail | `{{trigger.body.email}}` |
| Entreprise | Rechercher ou créer en fonction de `{{trigger.body.company}}` |
### Étape 5 : Tester le webhook
Avant l'activation, testez votre webhook :
**Avec cURL** :
```bash
curl -X POST https://api.twenty.com/webhooks/workflow/abc123... \
-H "Content-Type: application/json" \
-d '{"firstName":"Test","lastName":"User","email":"test@example.com"}'
```
**Avec Postman ou équivalent** :
1. Créez une requête POST vers votre URL de webhook
2. Définissez l'en-tête Content-Type sur `application/json`
3. Ajoutez votre corps JSON de test
4. Envoyez et vérifiez les exécutions du flux de travail
### Étape 6 : Activer
Une fois testé, cliquez sur **Activer** pour rendre le flux de travail actif.
## Gestion de différentes structures de données
### Données imbriquées
Si votre webhook envoie des données imbriquées :
```json
{
"contact": {
"name": "John Doe",
"email": "john@example.com"
},
"source": "website"
}
```
Faites référence avec : `{{trigger.body.contact.email}}`
### Tableaux
Si les données comprennent des tableaux :
```json
{
"items": [
{"name": "Product A", "qty": 2},
{"name": "Product B", "qty": 1}
]
}
```
La manière de gérer les tableaux dépend de votre cas d'utilisation :
**Nombre d'éléments inconnu → Utilisez Iterator**
Si vous devez traiter chaque élément du tableau (par exemple, créer un enregistrement pour chacun), ajoutez une action **Code** pour analyser le tableau, puis utilisez **Iterator** :
```javascript
export const main = async (params: { items: any }) => {
const items = typeof params.items === "string"
? JSON.parse(params.items)
: params.items;
return { items };
};
```
Utilisez ensuite Iterator pour itérer sur : `{{code.items}}`
**Champs connus/spécifiques → Extraire dans des champs nommés**
Si le tableau contient des champs spécifiques auxquels vous souhaitez accéder individuellement (par exemple, des réponses de formulaire où la position 0 est toujours "prénom", la position 1 est toujours "nom"), ajoutez une action **Code** pour les extraire :
```javascript
export const main = async (params: { items: any }) => {
const items = typeof params.items === "string"
? JSON.parse(params.items)
: params.items;
return {
product: {
name: items[0]?.name || "",
qty: items[0]?.qty || 0
}
};
};
```
Vous pouvez maintenant sélectionner `product.name` et `product.qty` individuellement dans les étapes suivantes.
<Tip>
Pour plus de détails sur la gestion des tableaux, voir [Gérer les tableaux dans les actions Code](/l/fr/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions).
</Tip>
## Connexes
* [Déclencheurs de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-triggers)
* [Actions de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,121 @@
---
title: Réponse automatique aux emails entrants
description: Créez un flux de travail qui utilise l'IA pour trier les emails entrants et envoyer automatiquement des réponses en fil de discussion.
---
Répondez aux emails entrants en quelques secondes — et non en heures. Ce flux de travail utilise un Agent AI pour filtrer le bruit (newsletters, spam, réponses automatiques) et rédiger une réponse personnalisée aux vrais messages. Ensuite, Amnesty International l'envoie comme une réponse filetée dans la conversation initiale.
## Comment fonctionne le fil de discussion des emails
Chaque email comporte un en-tête caché `Message-ID` — une empreinte unique attribuée par le serveur de messagerie de l'expéditeur. Lorsque vous répondez à un e-mail, votre client de messagerie définit un en-tête `In-Reply-To` faisant référence à cette empreinte. C’est ainsi que Gmail, Outlook et tous les autres clients regroupent les messages en fils de discussion.
Dans Twenty, cette empreinte est stockée en tant que `headerMessageId` sur l’objet Message. Votre flux de travail la récupère et la transmet au champ In-Reply-To de l’action Send Email.
## Création du flux de travail
### Étape 1 : Créer un nouveau flux de travail
Accédez à **Settings -> Workflows** et cliquez sur **+ New Workflow**.
### Étape 2 : Déclencher sur les messages entrants
Choisissez **When a Record is Created** puis **Messages**.
Chaque fois qu’un e-mail arrive dans Twenty, ceci se déclenche.
<img src="/images/user-guide/workflows/auto-reply/workflow-overview.png" style={{width:'100%'}} />
### Étape 3 : Identifier l’expéditeur
Ajoutez une action **Search Records**.
L’adresse de l’expéditeur n’est pas sur le message lui-même — elle se trouve sur l’enregistrement Message Participant associé.
| Champ | Valeur |
| ---------- | -------------------------------- |
| **Objet** | Participants au message |
| **Filtre** | Message **est** `{{trigger.id}}` |
| **Filtre** | Rôle **est** From |
| **Limite** | 1 |
Vous obtenez ainsi l’adresse e-mail de l’expéditeur dans `handle` et son nom dans `displayName`.
<img src="/images/user-guide/workflows/auto-reply/find-sender.png" style={{width:'100%'}} />
### Étape 4 : Triage et ébauche une réponse avec AI
Ajoutez une action **AI Agent**. Cette seule étape fait deux choses : elle détermine si l’e-mail mérite une réponse et, le cas échéant, en rédige une.
Utilisez une invite comme :
```
You are an email triage assistant for a sales team. Read the following
inbound email and decide if it deserves a reply.
Subject: {{trigger.subject}}
Body: {{trigger.text}}
From: {{Find Sender.first.displayName}} ({{Find Sender.first.handle}})
If this email is spam, a newsletter, an automated notification, or
otherwise does not need a human reply, respond with exactly: SKIP
Otherwise, write a short, professional reply (3-4 sentences max) that:
- Acknowledges their specific message
- Lets them know someone from the team will follow up shortly
- Is warm but not overly casual
Respond with only the reply text, no subject line or greeting prefix.
```
L’action AI Agent produit sa réponse dans un champ `response` auquel les étapes suivantes peuvent se référer.
<img src="/images/user-guide/workflows/auto-reply/ai-triage.png" style={{width:'100%'}} />
### Étape 5 : Bifurquer selon la décision de l’IA
Ajoutez une action **If/Else** pour vérifier si l’IA a décidé de répondre ou d’ignorer.
| Champ | Valeur |
| ------------- | ---------------------------------------------- |
| **Condition** | AI Agent `response` **ne contient pas** `SKIP` |
| **Si vrai** | Continuer vers Send Email |
| **Sinon** | Ne rien faire (le flux de travail se termine) |
Les spams, les newsletters et les messages générés automatiquement sont ignorés. Tout le reste passe à l’étape suivante.
<img src="/images/user-guide/workflows/auto-reply/should-reply.png" style={{width:'100%'}} />
### Étape 6 : Envoyer une réponse dans le fil de discussion
Ajoutez une action **Envoyer un e-mail** sur la branche « si vrai ». Cliquez sur **Options avancées**, puis sur **Ajouter In-Reply-To**.
| Champ | Valeur |
| -------------------- | -------------------------------------- |
| **À** | `{{Find Sender.first.handle}}` |
| **Objet** | `Re: {{trigger.subject}}` |
| **Corps du message** | `{{AI Triage & Draft Reply.response}}` |
| **In-Reply-To** | `{{trigger.headerMessageId}}` |
C’est le champ In-Reply-To qui fait de ce message une réponse plutôt qu’une nouvelle conversation. Le destinataire le voit regroupé sous l’e-mail d’origine dans Gmail, Outlook ou tout autre client.
<img src="/images/user-guide/workflows/auto-reply/send-email.png" style={{width:'100%'}} />
<Tip>
**In-Reply-To** attend un `message.headerMessageId` provenant du déclencheur — c’est l’empreinte unique de l’e-mail, pas une adresse de destinataire. Si vous le laissez vide, l’e-mail sera quand même envoyé, simplement comme un message autonome.
</Tip>
<Warning>
Gmail utilise l’objet pour regrouper les messages en fils de discussion. L’objet **doit** commencer par `Re:` (y compris les deux-points et l’espace) pour que Gmail affiche la réponse dans le fil d’origine. Sans cela, la réponse apparaîtra comme une conversation distincte — même si l’en-tête In-Reply-To est correctement défini.
</Warning>
### Étape 7 : Tester et activer
Cliquez sur **Test**, puis vérifiez votre client de messagerie. La réponse devrait apparaître imbriquée sous le message d’origine.
Activez lorsque vous en êtes satisfait.
## Idées pour aller plus loin
* **Ne répondre qu’aux VIP** — ajoutez une branche qui vérifie le domaine de l’expéditeur ou s’il existe en tant que Contact dans Twenty
* **Orienter selon l’intention** — utilisez des invites AI Agent distinctes pour traiter différemment les demandes commerciales et les demandes d’assistance
* **Enrichir avant de répondre** — ajoutez une étape Search Records pour intégrer l’entreprise de l’expéditeur ou l’historique d’opportunités dans l’invite d’IA afin d’obtenir des réponses plus personnalisées

View File

@@ -0,0 +1,179 @@
---
title: Automatisations « Closed Won »
description: Automatisez les activités post-gain lorsque des opportunités sont clôturées.
---
Lorsqu'une affaire se clôture, plusieurs actions doivent avoir lieu : mettre à jour le statut de l'entreprise, notifier les membres de l'équipe, créer des tâches d'intégration. Automatisez tout cela avec un seul flux de travail.
## Le problème
Lorsqu'une opportunité passe à "Closed Won":
* Le type d'entreprise doit passer de "Prospect" à "Customer"
* Des tâches d'intégration doivent être créées
* L'équipe Customer Success doit être informée
* Le commercial a besoin d'une confirmation
Faire cela manuellement est chronophage et sujet aux erreurs.
## La solution
Créez un flux de travail qui gère automatiquement toutes les activités post-gain.
## Configuration complète du flux de travail
### Étape 1 : Créer le flux de travail
1. Allez dans **Paramètres → Flux de travail**
2. Cliquez sur **+ Nouveau flux de travail**
3. Nommez-le "Affaire gagnée - Automatisation post-gain"
### Étape 2 : Configurer le déclencheur
1. Sélectionnez **L'enregistrement est mis à jour**
2. Choisissez **Opportunités**
3. Dans "Champs à surveiller", sélectionnez **Étape**
### Étape 3 : Ajouter un filtre d'étape
1. Ajoutez l'action **Filtre**
2. Condition : `{{trigger.object.stage}}` est égal à "Closed Won"
### Étape 4 : Mettre à jour le type d'entreprise
1. Ajoutez l'action **Mettre à jour l'enregistrement**
2. Configurer:
| Champ | Valeur |
| ------------------------------- | ------------------------------- |
| **Objet** | Entreprises |
| **Enregistrement** | `{{trigger.object.company.id}}` |
| **Type** | Client |
| **Date de la première affaire** | `{{trigger.object.closedAt}}` |
| **Propriétaire du compte** | `{{trigger.object.owner.id}}` |
### Étape 5 : Créer une tâche d'intégration
1. Ajoutez l'action **Créer un enregistrement**
2. Configurer:
| Champ | Valeur |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| **Objet** | Tâches |
| **Titre** | `Onboarding: {{trigger.object.name}}` |
| **Assigné(e)** | Membre de l'équipe Customer Success |
| **Date d'échéance** | Dans 3 jours |
| **Priorité** | Élevée |
| **Entreprise associée** | `{{trigger.object.company.id}}` |
| **Opportunité associée** | `{{trigger.object.id}}` |
| **Description** | `Intégration d'un nouveau client pour {{trigger.object.company.name}}. Valeur de l'affaire : {{trigger.object.amount}}` |
### Étape 6 : Informer Customer Success
1. Ajoutez l'action **Envoyer un e-mail**
2. Configurer:
| Champ | Valeur |
| -------------------- | -------------------------------------------------- |
| **À** | customer-success@yourcompany.com |
| **Objet** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Corps du message** | Voir l'exemple ci-dessous |
**Exemple de corps de l'e-mail** :
```
Hi CS Team,
We have a new customer!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
Let's give them a great start!
```
### Étape 7 : Confirmer au commercial
1. Ajoutez une autre action **Envoyer un e-mail**
2. Configurer:
| Champ | Valeur |
| -------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **À** | `{{trigger.object.owner.email}}` |
| **Objet** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Corps du message** | Félicitations ! Votre affaire a été traitée. L'équipe Customer Success a été informée et l'intégration a commencé. |
### Étape 8 : Tester et activer
1. Testez en déplaçant une opportunité de test vers "Closed Won"
2. Vérifier:
* Type d'entreprise modifié en "Customer"
* Tâche d'intégration créée
* L'équipe CS a reçu l'e-mail
* Le commercial a reçu la confirmation
3. Activez lorsque vous êtes prêt
## Gestion de "Closed Lost"
Créez un flux de travail similaire pour les affaires perdues :
### Déclencheur
* Enregistrement mis à jour (Opportunités, Étape = "Closed Lost")
### Actions
1. **Créer un enregistrement** : Tâche pour "Analyse d'affaire perdue"
2. **Mettre à jour l'enregistrement** : Ajouter la raison de perte à l'enregistrement de l'entreprise
3. **Envoyer un e-mail** : Informer le responsable de l'affaire perdue
## Avancé : intégration en plusieurs étapes
Pour une intégration complexe, créez plusieurs tâches :
```javascript
export const main = async (params) => {
const tasks = [
{ title: "Welcome call", daysFromNow: 1, assignee: "CS" },
{ title: "Send onboarding materials", daysFromNow: 2, assignee: "CS" },
{ title: "Technical setup", daysFromNow: 5, assignee: "Support" },
{ title: "30-day check-in", daysFromNow: 30, assignee: "CS" }
];
return { tasks };
};
```
Utilisez **Iterator** pour créer chaque tâche à partir du tableau.
## Idées de personnalisation
### Maintenez vos autres outils à jour
* Créez un client dans le système de facturation avec une action **HTTP Request**
### Actions conditionnelles
Utilisez des actions **Filtre** pour :
* Intégration différente pour entreprises vs PME
* Assignés différents selon la région
* Ignorer les notifications pour les petites affaires
### Inclure les détails de la transaction
Utilisez l'action **Code** pour formater :
* Documents de synthèse de l'affaire
* Notes de passation pour l'équipe CS
* Listes de contrôle d'intégration personnalisées
## Articles connexes
* [Actions de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Envoyer des e-mails depuis des flux de travail](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,136 @@
---
title: Détecter les opportunités stagnantes
description: Avertissez automatiquement les responsables lorsque les opportunités n'ont pas été mises à jour.
---
Maintenez la santé de votre pipeline en alertant les responsables lorsque des opportunités deviennent stagnantes. Ce flux de travail vérifie les opportunités qui n'ont pas été mises à jour depuis un nombre de jours défini.
## Le problème
Des opportunités laissées sans mise à jour entraînent :
* Affaires qui se refroidissent
* Prévisions peu fiables
* Perte de revenus
## La solution
Créez un flux de travail planifié qui identifie les opportunités stagnantes et envoie un e-mail à leurs responsables.
## Configuration pas à pas
### Étape 1 : Créer le flux de travail
1. Allez dans **Paramètres → Flux de travail**
2. Cliquez sur **+ Nouveau flux de travail**
3. Nommez-le "Alerte d'opportunités stagnantes"
### Étape 2 : Configurer le déclencheur
1. Sélectionnez **Selon un calendrier**
2. Définissez l'exécution quotidienne (par ex., chaque jour à 8 h)
### Étape 3 : Rechercher les opportunités stagnantes
1. Ajoutez l'action **Rechercher des enregistrements**
2. Configurer:
| Champ | Valeur |
| ---------- | ------------------------------------------------------- |
| **Objet** | Opportunités |
| **Filtre** | Updated At est antérieur à (today - 7 days) |
| **Filtre** | Stage n'est pas "Closed Won" ET n'est pas "Closed Lost" |
| **Limite** | 100 |
### Étape 4 : Vérifier si des résultats ont été trouvés
1. Ajoutez l'action **Filtre**
2. Condition : `{{searchRecords.length}}` est supérieur à 0
3. S'il n'y a pas d'opportunités stagnantes, le flux de travail s'arrête ici
### Étape 5 : Formater l'alerte (action Code)
Ajoutez une action **Code** pour formater l'e-mail :
```javascript
export const main = async (params) => {
const opportunities = params.opportunities;
// Group opportunities by owner
const byOwner = {};
opportunities.forEach(opp => {
const ownerEmail = opp.owner?.email || 'unassigned';
if (!byOwner[ownerEmail]) {
byOwner[ownerEmail] = [];
}
byOwner[ownerEmail].push({
name: opp.name,
amount: opp.amount,
lastUpdated: opp.updatedAt,
stage: opp.stage
});
});
// Format summary for manager
let summary = "Stale Opportunities Report\n\n";
Object.entries(byOwner).forEach(([owner, opps]) => {
summary += `${owner}: ${opps.length} stale opportunities\n`;
opps.forEach(opp => {
summary += ` - ${opp.name} (${opp.stage})\n`;
});
summary += "\n";
});
return {
summary,
totalCount: opportunities.length
};
};
```
### Étape 6 : Envoyer l'e-mail d'alerte
Ajoutez l'action **Envoyer un e-mail** :
| Champ | Valeur |
| -------------------- | ---------------------------------------------------------------------------- |
| **À** | sales-manager@yourcompany.com |
| **Objet** | `🚨 {{code.totalCount}} opportunités stagnantes nécessitent votre attention` |
| **Corps du message** | `{{code.summary}}` |
### Étape 7 : Tester et activer
1. Cliquez sur **Test** pour exécuter le flux de travail
2. Vérifiez que l'e-mail contient les bonnes données
3. Activez lorsque vous êtes prêt
## Options de personnalisation
### Modifier le seuil de stagnation
Modifiez le filtre Rechercher des enregistrements pour changer de 7 jours à la période de votre choix :
* 3 jours pour les ventes à cadence élevée
* 14 jours pour les affaires grands comptes
* 30 jours pour les cycles de vente longs
### Alerter les commerciaux individuellement
Au lieu d'un seul e-mail au responsable, utilisez **Iterator** pour envoyer des e-mails personnalisés à chaque commercial au sujet de ses propres affaires stagnantes.
### Ajouter une escalade
Créez plusieurs flux de travail avec une sévérité croissante :
1. Jour 7 : e-mail au commercial
2. Jour 14 : e-mail au commercial + au responsable
3. Jour 21 : créer une tâche pour que le responsable intervienne
### Inclure dans Slack
Utilisez **HTTP Request** pour publier vers un webhook Slack à la place d'un e-mail ou en complément.
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Envoyer des e-mails depuis des workflows](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,69 @@
---
title: Afficher le nombre d'e-mails reçus
description: Créez un workflow pour compter et afficher automatiquement le nombre d'e-mails reçus de chaque contact.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/workflow-number-of-emails-received.png" style={{width:'100%'}} />
## Vue d'ensemble
Ce workflow se déclenche chaque fois qu'un nouvel e-mail est reçu et met à jour un champ personnalisé sur l'enregistrement Person avec le nombre total d'e-mails provenant de cet expéditeur.
## Prérequis
Avant de configurer ce workflow, créez un champ personnalisé sur l'objet **People** :
1. Allez dans **Paramètres → Modèle de données → People**
2. Ajoutez un nouveau champ **Nombre**
3. Nommez-le par exemple "Nombre d'e-mails reçus de cette personne"
## Configuration pas à pas
<VimeoEmbed videoId="1148700336" title="Démonstration vidéo" />
### Étape 1 : Configurer le déclencheur
1. Allez dans **Workflows** et créez un nouveau workflow
2. Sélectionnez **Record is Created** comme déclencheur
3. Choisissez **Message Participants** (disponible sous Advanced objects)
<Note>
Un Message Participant est une combinaison d'un ID de message et d'un ID de personne, créant un enregistrement unique par message. Ceci est plus facile à suivre que les Messages directement, car nous pouvons accéder au champ `handle`, qui contient l'adresse e-mail de l'expéditeur (ou du destinataire).
</Note>
### Étape 2 : Filtrer sur le rôle
1. Ajoutez l'action **Filtre**
2. Définissez la condition : **Role** est égal à **FROM**
Cela garantit que vous ne comptez que les messages envoyés par cette personne, et non les messages qui lui sont envoyés.
### Étape 3 : Rechercher tous les Message Participants avec le même handle
1. Ajoutez une action **Search Records**
2. Sélectionnez **Message Participants** comme objet
3. Ajoutez des filtres : **Handle** est égal au handle du déclencheur (l'adresse e-mail de l'expéditeur) et **Role** est égal à **FROM**
4. Augmentez **Limit** de 1 à **200** (le maximum)
Cela trouve tous les messages provenant de cette adresse e-mail afin d'obtenir le nombre total.
<Note>
L'action Search Records est limitée à retourner 200 enregistrements maximum. Cependant, comme vous utilisez uniquement la valeur `totalCount` (et non les enregistrements individuels), cette étape renverra le nombre total d'e-mails envoyés par cette personne.
</Note>
### Étape 4 : Mettre à jour l'enregistrement Person avec une action Create or Update Record
1. Ajoutez une action **Créer ou mettre à jour un enregistrement**
2. Sélectionnez **Personnes** comme objet
3. Recherchez la personne en faisant correspondre son e-mail au `handle` du participant au message
4. Définissez votre champ personnalisé "Nombre d'e-mails reçus" sur `{{searchRecords.totalCount}}`
La valeur `totalCount` de l'action Rechercher des enregistrements représente le nombre total d'e-mails reçus de cette personne.
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Comment créer des champs personnalisés](/l/fr/user-guide/data-model/how-tos/customize-your-data-model)
* [Action Search Records](/l/fr/user-guide/workflows/capabilities/workflow-actions#search-records)

View File

@@ -0,0 +1,170 @@
---
title: Afficher les données des enregistrements liés
description: Afficher les données provenant d'enregistrements liés (p. ex., informations sur l'entreprise dans les Opportunités) à l'aide de workflows.
---
Afficher les données d'enregistrements liés directement sur vos enregistrements — par exemple, afficher l'effectif d'une Entreprise dans ses Opportunités. Cette solution de contournement par workflow est utile jusqu'à ce que les champs imbriqués soient disponibles nativement.
## Cas d'utilisation courants
| Source | Destination | Champs à copier |
| ----------- | ----------- | --------------------------------------------------------- |
| Entreprise | Opportunité | Secteur, Taille de l'entreprise, ARR |
| Personne | Opportunité | E-mail, Téléphone, Titre du poste |
| Opportunité | Entreprise | Montant du dernier contrat, Date du dernier contrat gagné |
## Copie de champ simple
### Exemple : Copier l'e-mail du contact vers l'Opportunité
**Objectif** : Lors de la définition d'un Point de contact sur une opportunité, copier son e-mail dans l'opportunité pour un accès facile.
### Prérequis
Créez les champs de destination dans **Paramètres → Modèle de données → Opportunités** avant de créer le workflow :
* E-mail du contact (type : E-mail)
* Téléphone du contact (type : Téléphone)
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Opportunités, champ Point de contact)
2. **Filtre** : Vérifiez que le Point de contact n'est pas vide
3. **Rechercher des enregistrements** : Trouver la personne liée
* Objet : Personnes
* Filtre : ID égal à `{{trigger.object.pointOfContact.id}}`
4. **Mettre à jour un enregistrement** :
* Objet : Opportunités
* Enregistrement : `{{trigger.object.id}}`
* E-mail du contact : `{{searchRecords[0].email}}`
* Téléphone du contact : `{{searchRecords[0].phone}}`
## Copier plusieurs champs
### Exemple : Synchroniser les informations de l'entreprise vers toutes les Opportunités liées
**Objectif** : Lorsque les détails de l'entreprise changent, mettre à jour toutes les opportunités liées.
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Entreprises)
* Champs : Secteur, Taille de l'entreprise, Chiffre d'affaires annuel
2. **Rechercher des enregistrements** : Trouver toutes les opportunités pour cette entreprise
* Objet : Opportunités
* Filtre : ID de l'entreprise égal à `{{trigger.object.id}}`
3. **Iterator** : Parcourir chaque opportunité
4. **Mettre à jour un enregistrement** (dans l'Iterator) :
* Objet : Opportunités
* Enregistrement : `{{iterator.currentItem.id}}`
* Secteur de l'entreprise : `{{trigger.object.industry}}`
* Taille de l'entreprise : `{{trigger.object.companySize}}`
* ARR de l'entreprise : `{{trigger.object.annualRevenue}}`
## Copier lors de la création d'un enregistrement
### Exemple : Préremplir l'Opportunité avec les données de l'entreprise
**Objectif** : Lors de la création d'une opportunité liée à une entreprise, copier automatiquement les informations clés de l'entreprise.
### Prérequis
Créez les champs de destination dans **Paramètres → Modèle de données → Opportunités** :
* Secteur de l'entreprise (type : Texte)
* Taille de l'entreprise (type : Nombre)
### Installation
1. **Déclencheur** : Enregistrement créé (Opportunités)
* Filtre : L'entreprise n'est pas vide
2. **Rechercher des enregistrements** : Récupérer les détails de l'entreprise liée
* Objet : Entreprises
* Filtre : ID égal à `{{trigger.object.company.id}}`
3. **Mettre à jour un enregistrement** :
* Objet : Opportunités
* Enregistrement : `{{trigger.object.id}}`
* Secteur de l'entreprise : `{{searchRecords[0].industry}}`
* Taille de l'entreprise : `{{searchRecords[0].employees}}`
<Note>
**Limitation concernant les tâches et les notes** : Les relations sur les tâches et les notes sont codées en dur en plusieurs-à-plusieurs et ne sont pas encore disponibles dans les déclencheurs ou actions de workflows. Pour accéder à ces relations, utilisez plutôt l'[API](/l/fr/developers/extend/api).
</Note>
## Synchronisation bidirectionnelle
### Exemple : Maintenir le contact principal synchronisé
**Objectif** : Lorsque le contact principal d'une entreprise change, mettre à jour le contact. Lorsqu'une personne devient contact principal, mettre à jour l'entreprise.
### Workflow 1 : Entreprise → Personne
1. **Déclencheur** : Enregistrement mis à jour (Entreprises, champ Contact principal)
2. **Mettre à jour un enregistrement** : Définir le champ "Is Primary Contact" de la personne sur true
3. **Rechercher des enregistrements** : Trouver le contact principal précédent
4. **Mettre à jour un enregistrement** : Définir le "Is Primary Contact" de l'ancien contact sur false
### Workflow 2 : Personne → Entreprise
1. **Déclencheur** : Enregistrement mis à jour (Personnes, Is Primary Contact = true)
2. **Mettre à jour un enregistrement** : Définir le contact principal de l'entreprise sur cette personne
<Note>
Soyez prudent avec les synchronisations bidirectionnelles pour éviter les boucles infinies. Utilisez des filtres pour vérifier si la valeur a réellement changé avant la mise à jour.
</Note>
## Utiliser du code pour des mappages complexes
### Exemple : Transformer les données lors de la copie
**Objectif** : Copier et mettre en forme le numéro de téléphone de la personne vers l'opportunité.
```javascript
export const main = async (params) => {
const { phone } = params;
if (!phone) return { formattedPhone: null };
// Remove non-numeric characters
const digits = phone.replace(/\D/g, '');
// Format as (XXX) XXX-XXXX
const formatted = digits.length === 10
? `(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`
: phone;
return { formattedPhone: formatted };
};
```
## Meilleures pratiques
### Évitez les boucles
* Ne créez pas de workflows qui se déclenchent mutuellement à l'infini
* Utilisez des conditions spécifiques sur les champs
* Ajoutez des vérifications pour voir si la valeur a réellement changé
### Gérer les données manquantes
* Vérifiez toujours que l'enregistrement source existe avant de copier
* Fournissez des valeurs par défaut pour les champs facultatifs
* Utilisez des filtres pour ignorer lorsque le champ source est vide
### Performances
* Regroupez les mises à jour lors de la copie vers de nombreux enregistrements
* Utilisez des workflows planifiés pour les opérations de synchronisation en masse
* Envisagez d'utiliser Iterator pour les mises à jour de plusieurs enregistrements
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Déclencheurs de workflows](/l/fr/user-guide/workflows/capabilities/workflow-triggers)

View File

@@ -0,0 +1,202 @@
---
title: Champs de formule
description: Créez des champs de formule à l'aide des workflows jusqu'à ce que la prise en charge native soit disponible.
---
Vingt ne supporte pas encore les champs de formules natives (à venir en 2026), mais vous pouvez obtenir le même résultat en utilisant les workflows. Cette solution de contournement vous permet de calculer et de renseigner automatiquement les valeurs de champs — des concaténations simples à une logique métier complexe.
## Cas d'utilisation courants
| Cas d'utilisation | Exemple de formule |
| -------------------------- | ---------------------------------------- |
| **Nom complet** | Prénom + " " + Nom de famille |
| **Montant attendu** | Montant × Probabilité |
| **Jours avant l'échéance** | Date d'échéance - Aujourd'hui |
| **Jours dans l'étape** | Aujourd'hui - Date d'entrée dans l'étape |
| **Score de lead** | Points basés sur plusieurs critères |
<Tip>
Pour un exemple complet de suivi du temps dans les étapes du pipeline, voir [Suivre la durée de présence des opportunités dans chaque étape](/l/fr/user-guide/views-pipelines/how-tos/track-time-in-stage).
</Tip>
## Formule de base : concaténation
### Exemple : remplissage automatique du nom complet
**Objectif** : Combiner automatiquement le prénom et le nom de famille dans un champ de nom complet.
### Installation
1. **Déclencheur** : Enregistrement mis à jour ou créé (Personnes)
2. **Filtre** : Vérifier que le prénom ou le nom de famille a changé
3. **Action de code** :
```javascript
export const main = async (params) => {
const { firstName, lastName } = params;
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ');
return { fullName };
};
```
4. **Mettre à jour l'enregistrement** : Définir le nom complet sur `{{code.fullName}}`
## Formule numérique : montant attendu
### Exemple : calculer le revenu attendu
**Objectif** : Multiplier le montant de l'opportunité par la probabilité pour obtenir le montant attendu.
Voir [Comment afficher le montant attendu dans le pipeline](/l/fr/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline) pour le workflow complet.
### Configuration
1. **Déclencheur** : Enregistrement mis à jour (Opportunités, champ Montant OU Probabilité)
2. **Action de code** :
```javascript
export const main = async (params) => {
const { amount, probability } = params;
const expectedAmount = (amount || 0) * (probability || 0) / 100;
return { expectedAmount };
};
```
3. **Mettre à jour l'enregistrement** : Définir le montant attendu sur `{{code.expectedAmount}}`
## Formule de date : calcul des jours
### Exemple : jours avant l'échéance de la tâche
**Objectif** : Calculer le nombre de jours restants avant la date d'échéance d'une tâche.
### Installation
1. **Déclencheur** : Enregistrement mis à jour ou créé (Tâches, champ Date d'échéance)
2. **Action de code** :
```javascript
export const main = async (params) => {
const { dueDate } = params;
if (!dueDate) {
return { daysUntilDue: null };
}
const due = new Date(dueDate);
const today = new Date();
const diffTime = due - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return { daysUntilDue: diffDays };
};
```
3. **Mettre à jour l'enregistrement** : Définir les jours avant l'échéance sur `{{code.daysUntilDue}}`
<Note>
Des valeurs négatives indiquent des tâches en retard. Vous pouvez utiliser ce champ pour filtrer ou trier les tâches par urgence.
</Note>
## Formule conditionnelle : score de lead
### Exemple : calculer le score de lead en fonction de critères
**Objectif** : Noter les leads selon la taille de l'entreprise, le secteur d'activité et l'engagement.
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Personnes ou Entreprises)
2. **Action de code** :
```javascript
export const main = async (params) => {
const { companySize, industry, hasEmail, hasPhone, source } = params;
let score = 0;
// Company size scoring
if (companySize === 'Enterprise') score += 30;
else if (companySize === 'Mid-Market') score += 20;
else if (companySize === 'SMB') score += 10;
// Industry scoring
const targetIndustries = ['Technology', 'Finance', 'Healthcare'];
if (targetIndustries.includes(industry)) score += 25;
// Contact info scoring
if (hasEmail) score += 10;
if (hasPhone) score += 15;
// Source scoring
if (source === 'Referral') score += 20;
else if (source === 'Website') score += 10;
return { leadScore: score };
};
```
3. **Mettre à jour l'enregistrement** : Définir le score de lead sur `{{code.leadScore}}`
## Formule texte : extraction de domaine
### Exemple : extraire le domaine à partir de l'email
**Objectif** : Extraire et stocker automatiquement le domaine de l'email.
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Personnes, champ Email)
2. **Action de code** :
```javascript
export const main = async (params) => {
const { email } = params;
if (!email) return { domain: null };
const domain = email.split('@')[1]?.toLowerCase();
return { domain };
};
```
3. **Mettre à jour l'enregistrement** : Définir le champ Domaine sur `{{code.domain}}`
## Meilleures pratiques
### Performances
* Ne déclenchez que lors des modifications de champs pertinentes
* Utilisez des filtres pour ignorer les enregistrements qui n'ont pas besoin de calcul
* Évitez les calculs complexes dans les workflows à fort volume
### Gestion des erreurs
* Vérifiez les valeurs null/undefined avant les calculs
* Utilisez des valeurs par défaut lorsque des données manquent
* Renvoyez des messages d'erreur clairs lorsque les calculs échouent
### Tests
* Testez avec des cas limites (champs vides, valeurs zéro)
* Vérifiez manuellement les calculs avant l'activation
* Surveillez les exécutions de workflows pour détecter des résultats inattendus
## Articles connexes
* [Comment afficher le montant attendu dans le pipeline](/l/fr/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline)
* [Comment suivre le temps par étape](/l/fr/user-guide/views-pipelines/how-tos/track-time-in-stage)
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,127 @@
---
title: Notifier les collègues d'une note à relire
description: Envoyez des notifications par e-mail automatiques lorsque vous assignez un collègue pour relire une note.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<Note>
**La mention directe des collègues dans les notes n'est pas encore prise en charge.** Cette fonctionnalité est prévue pour le second semestre 2026. En attendant, ce workflow constitue une solution de contournement pratique.
</Note>
## Le défi
Vous souhaitez mentionner un collègue dans une note afin qu'il reçoive une notification pour la relire. À ce jour, Twenty ne prend pas en charge les @mentions dans les notes ou les tâches.
## La solution de contournement
Au lieu de mentionner dans le texte de la note, vous pouvez :
1. Créez un champ de relation **Relecteur** sur l'objet Notes
2. Attribuez le relecteur lors de la création ou de la modification de la note
3. Avoir un flux de travail envoyer automatiquement un e-mail au réviseur avec un lien vers la note
<Warning>
**Limitation** : Cette approche permet d'attribuer un seul relecteur par champ. Si vous devez notifier plusieurs personnes, créez des champs de relation supplémentaires (par ex. : Relecteur 1, Relecteur 2). Cela ne s'adaptera pas bien si vous devez mentionner de nombreux collaborateurs à la fois.
</Warning>
## Configuration pas à pas
### Étape 1 : Créer le champ Relecteur
<img src="/images/user-guide/workflows/reviewer-field-for-note.png" style={{width:'100%'}} />
1. Allez dans **Paramètres → Modèle de données → Notes**
2. Cliquez sur **Ajouter un champ**
3. Configurer :
| Paramètre | Valeur |
| -------------------- | -------------------------------------------------- |
| **Name** | Relecteur |
| **Type** | Relation |
| **Objet lié** | Membre de l'espace de travail |
| **Type de relation** | Plusieurs notes → Un membre de l'espace de travail |
4. Enregistrez le champ
### Étape 2 : Créer le workflow
<VimeoEmbed videoId="1151897168" title="Démonstration vidéo" />
#### Créer le déclencheur du workflow
1. Allez dans la vue principale et créez un nouveau workflow
2. Sélectionnez **Record Updated** comme déclencheur
3. Choisissez **Note** comme objet et indiquez le champ Relecteur.
4. Cela se déclenchera chaque fois que le relecteur d'une note est mis à jour.
#### Ajouter un filtre pour vérifier qu'un relecteur vient d'être attribué
Ajoutez une action **Filtre** pour garantir que le workflow ne continue que lorsqu'un relecteur est attribué :
| Condition | Valeur |
| ------------- | -------------- |
| **Relecteur** | n'est pas vide |
#### Rechercher le membre de l'espace de travail
1. Ajoutez une action **Search Records**
2. Sélectionnez **Membre de l'espace de travail** (dans les objets avancés)
3. Ajoutez un filtre :
| Condition | Valeur |
| --------- | ---------------------------- |
| **ID** | égal à `{{note.reviewerId}}` |
Cela récupère l'enregistrement complet du membre de l'espace de travail, y compris son adresse e-mail et son nom.
#### Envoyer l'e-mail de notification
1. Ajoutez l'action **Envoyer un e-mail**
2. Configurer :
| Champ | Valeur |
| -------------------- | --------------------------------------------------- |
| **À** | Search Record → First Workspace Member → User Email |
| **Objet** | Une note nécessite votre relecture |
| **Corps du message** | Voir l'exemple ci-dessous |
**Exemple de corps de l'e-mail :**
```text
Hi {{searchRecord.firstWorkspaceMember.name.firstName}},
You've been assigned to review a note.
View the note here:
https://yourSubDomain.twenty.com/object/note/{{recordIsUpdated.id}}
Best,
Twenty
```
<Note>
Remplacez `yourSubDomain` par le sous-domaine réel de votre espace de travail Twenty.
</Note>
#### Tester et activer
1. Cliquez sur **Test** pour exécuter le workflow manuellement
2. Créez ou modifiez une note et attribuez un relecteur
3. Vérifiez la boîte de réception du relecteur pour l'e-mail de notification
4. Activez le flux de travail
## Utilisation
Une fois configuré, votre équipe peut utiliser ce workflow de la manière suivante :
1. Créer une nouvelle note (ou en modifier une existante)
2. Sélectionner un collègue dans le champ **Relecteur**
3. Le collègue reçoit automatiquement un e-mail avec un lien direct vers la note
## Articles connexes
* [Déclencheurs de workflows](/l/fr/user-guide/workflows/capabilities/workflow-triggers)
* [Envoyer des e-mails depuis des flux de travail](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Créer des champs personnalisés](/l/fr/user-guide/data-model/how-tos/create-custom-fields)
* [Créer des champs de relation](/l/fr/user-guide/data-model/how-tos/create-relation-fields)

View File

@@ -0,0 +1,106 @@
---
title: Envoyer des alertes par e-mail pour les tâches arrivant à échéance
description: Informer automatiquement les membres de l'équipe de leurs tâches à venir ou en retard.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/email_tasks_due.png" style={{width:'100%'}} />
Envoyer des rappels quotidiens par e-mail à chaque membre de l'équipe concernant ses tâches arrivant à échéance aujourd'hui.
## Vue d'ensemble
Ce workflow s'exécute selon un calendrier et :
1. Récupère tous les membres de l'espace de travail
2. Parcourt chaque membre
3. Trouve ses tâches arrivant à échéance aujourd'hui
4. Formate et envoie un e-mail personnalisé
## Configuration pas à pas
<VimeoEmbed videoId="1147463514" title="Démonstration vidéo" />
### Étape 1 : Configurer le déclencheur
1. Allez dans **Paramètres → Workflows** et créez un nouveau workflow
2. Sélectionnez **Selon un calendrier** comme déclencheur
3. Utilisez une expression cron pour tous les jours à 8:00 : `0 8 * * *`
### Étape 2 : Rechercher tous les membres de l'espace de travail
1. Ajoutez une action **Rechercher des enregistrements**
2. Sélectionnez **Membres de l'espace de travail** (dans les objets avancés)
3. Aucun filtre nécessaire — cela renvoie tous les membres
### Étape 3 : Ajouter un itérateur
1. Ajoutez une action **Itérateur**
2. Définissez le tableau d'entrée sur les membres de l'espace de travail de l'étape précédente
3. Toutes les actions à l'intérieur de l'itérateur s'exécuteront une fois par membre
### Étape 4 : Rechercher les tâches arrivant à échéance aujourd'hui (dans l'itérateur)
1. À l'intérieur de l'itérateur, ajoutez une action **Rechercher des enregistrements**
2. Sélectionnez **Tâches** comme objet
3. Ajoutez des filtres :
* **Assigné** = membre actuel de l'espace de travail (depuis l'itérateur)
* **Date d'échéance** = aujourd'hui
### Étape 5 : Mettre en forme les tâches dans le corps de l'e-mail (dans l'itérateur)
Ajoutez une action **Code** pour mettre en forme les tâches en une liste lisible avec des liens :
```javascript
export const main = async (params: {
tasksDue?: Array<{ id: string; title: string }> | null | string;
}) => {
const tasksDue =
typeof params.tasksDue === "string"
? JSON.parse(params.tasksDue)
: params.tasksDue;
if (!Array.isArray(tasksDue) || tasksDue.length === 0) {
return {
formattedTasks: "No tasks due today."
};
}
const formattedTasks = tasksDue
.map(
t =>
`${t.title}\nhttps://yourSubDomain.twenty.com/object/task/${t.id}`
)
.join("\n\n");
return { formattedTasks };
};
```
<Note>
Remplacez `yourSubDomain` par le sous-domaine réel de votre espace de travail Twenty.
</Note>
### Étape 6 : Envoyer un e-mail (dans l'itérateur)
1. Ajoutez une action **Envoyer un e-mail** (toujours dans l'itérateur)
2. Configurer:
| Champ | Valeur |
| -------------------- | ------------------------------------------------------------------------------ |
| **À** | `{{iterator.currentItem.userEmail}}` (e-mail du membre de l'espace de travail) |
| **Objet** | Vos tâches arrivant à échéance aujourd'hui |
| **Corps du message** | `{{code.formattedTasks}}` |
### Étape 7 : Tester et activer
1. Cliquez sur **Test** pour exécuter le workflow manuellement
2. Vérifiez les boîtes de réception pour les e-mails
3. Activez le workflow
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Envoyer des e-mails depuis des workflows](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Gérer les tableaux dans les actions Code](/l/fr/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,21 @@
---
title: Services Professionnels
description: Obtenez de l'aide professionnelle pour créer des workflows et automatisations complexes grâce à l'équipe de Twenty et à ses partenaires certifiés.
---
## Quand avez-vous besoin d'une aide professionnelle ?
Considérez les services professionnels pour :
* Intégrations complexes de plusieurs systèmes
* Logique métier avancée et règles d'automatisation
* Workflows de traitement de données à grande échelle
* Développement d'API sur mesure
* Formation d'équipe et optimisation des workflows
* Quand vous n'avez pas de ressources internes
## Options de Service
### Partenaires pour l'Implémentation
Collaborez avec des partenaires certifiés pour des personnalisations avancées. Contactez-nous à [contact@twenty.com](mailto:contact@twenty.com) pour entrer en contact avec nos [partenaires de mise en œuvre](https://twenty.com/partners).

View File

@@ -0,0 +1,170 @@
---
title: Dépannage du Flux de Travail
description: Problèmes courants des flux de travail et comment les résoudre.
---
## Problèmes Courants et Solutions
### Flux de Travail Ne Se Déclenchant Pas
**Symptômes**: Votre flux de travail ne s'exécute pas lorsque vous vous y attendez.
**Causes possibles**:
1. **Flux de travail non activé**: Assurez-vous que le flux de travail est défini sur "Active" et non sur "Draft"
2. **Conditions de déclenchement non remplies**: Vérifiez que le déclencheur correspond à l'événement attendu
3. **Champ non surveillé**: Pour les déclencheurs "Record is Updated", assurez-vous que le champ spécifique est surveillé
4. **Autorisations**: Vérifiez que vous avez l'autorisation d'exécuter des flux de travail
**Solutions** :
* Vérifiez le statut du flux de travail dans la liste des flux de travail
* Testez avec l'action spécifique qui est censée le déclencher
* Passez en revue la configuration du déclencheur
* Contactez votre administrateur au sujet des autorisations
### Déclenchement du flux de travail trop tôt (champs vides)
**Symptômes**: Lors de la création manuelle d'un enregistrement dans l'interface, votre flux de travail se déclenche avant que vous n'ayez eu le temps de remplir tous les champs. Le flux de travail s'exécute alors que la plupart des champs sont vides.
**Pourquoi cela se produit**: Twenty enregistre tout en temps réel — il n'y a pas de mode "édition" distinct du mode "lecture". Lorsque vous créez un enregistrement, il est enregistré immédiatement, déclenchant l'événement "Record is created" avant que vous ne puissiez remplir des champs supplémentaires.
**Quand "Record is created" fonctionne bien**:
* Enregistrements créés via des appels API (les champs sont renseignés en une seule requête)
* Enregistrements créés via une importation
* Création automatisée d'enregistrements à partir d'autres flux de travail
**Solution**: Pour les enregistrements créés manuellement dans l'interface, utilisez plutôt **"Record is created or updated"** comme déclencheur. Ainsi:
* Le flux de travail se déclenche après que l'utilisateur a fini de remplir et d'enregistrer les champs
* Vous obtenez des données complètes plutôt que des valeurs vides
<Tip>
Si vous souhaitez que le flux de travail ne s'exécute qu'une seule fois par enregistrement, ajoutez une action Filter pour vérifier un champ comme `createdAt equals updatedAt` (première sauvegarde) ou utilisez un champ à case à cocher personnalisé pour suivre si le flux de travail s'est déjà exécuté.
</Tip>
### Actions en échec
**Symptômes**: Le flux de travail s'exécute mais certaines actions échouent.
**Causes possibles**:
1. **Données manquantes**: Les champs requis sont vides
2. **Références invalides**: Les variables des étapes précédentes n'existent pas
3. **Erreurs d'API**: Les services externes renvoient des erreurs
4. **Problèmes d'autorisations**: L'action requiert des autorisations que vous n'avez pas
**Solutions** :
* Examinez les détails de l'exécution du flux de travail pour trouver des messages d'erreur
* Vérifiez que tous les champs requis ont des valeurs
* Testez les connexions à l'API indépendamment
* Passez en revue les autorisations des rôles
### Erreurs de requête HTTP
**Symptômes**: Les actions HTTP Request échouent ou renvoient des résultats inattendus.
**Codes d'erreur courants**:
* **400**: Mauvaise requête - vérifiez le format du corps de la requête
* **401**: Non autorisé - vérifiez la clé API
* **403**: Interdit - vérifiez les autorisations de l'API
* **404**: Introuvable - vérifiez l'URL du point de terminaison
* **429**: Trop de requêtes - mettez en place une limitation de débit
* **500**: Erreur serveur - problème du service externe
**Solutions** :
* Vérifiez l'URL du point de terminaison de l'API
* Vérifiez les en-têtes d'authentification
* Testez d'abord l'appel d'API en dehors de Twenty
* Ajoutez une gestion des erreurs dans les actions Code
### Erreurs des actions Code
**Symptômes**: Le code JavaScript ne s'exécute pas.
**Problèmes courants**:
1. **Erreurs de syntaxe**: Fautes de frappe ou JavaScript invalide
2. **Variables non définies**: Faire référence à des variables qui n'existent pas
3. **Erreurs de type**: Opérations sur des types de données incorrects
4. **Dépassements de délai**: Le code met trop de temps à s'exécuter
**Solutions** :
* Utilisez la validation intégrée de l'éditeur de code
* Testez d'abord la logique du code dans une console JavaScript
* Ajoutez des instructions console.log pour le débogage
* Simplifiez les opérations complexes
### E-mails non envoyés
**Symptômes**: L'action Send Email ne délivre pas les e-mails.
**Causes possibles**:
1. **Aucun compte e-mail connecté**: Vérifiez Settings → Accounts
2. **Adresse e-mail invalide**: L'adresse e-mail du destinataire est mal formée
3. **Limites d'envoi**: Limites de débit du fournisseur d'e-mails atteintes
4. **Filtres anti-spam**: Les e-mails sont bloqués
**Solutions** :
* Vérifiez la connexion du compte e-mail
* Validez les adresses e-mail des destinataires
* Vérifiez les limites du fournisseur d'e-mails
* Examinez le contenu de l'e-mail pour détecter des déclencheurs de spam
## Débogage des flux de travail
### Utiliser les exécutions de flux de travail
1. Accédez à l'éditeur de flux de travail
2. Ouvrez le panneau **Runs**
3. Trouvez l'exécution en échec
4. Cliquez pour voir les détails étape par étape
5. Passez en revue les messages d'erreur et les données de sortie
### Tester les étapes individuellement
1. Pour les actions Code, utilisez le bouton **Test**
2. Pour les requêtes HTTP, testez le point de terminaison séparément
3. Créez des enregistrements de test pour déclencher des flux de travail
4. Utilisez des déclencheurs manuels pour des tests contrôlés
### Schémas de débogage courants
**Ajoutez des logs**:
Utilisez des actions Code pour consigner des valeurs intermédiaires à des fins de débogage.
**Isolez les étapes**:
Testez chaque étape indépendamment pour identifier les échecs.
**Vérifiez le flux de données**:
Assurez-vous que chaque étape reçoit les données d'entrée attendues.
## Bonnes pratiques pour éviter les problèmes
### Avant l'activation
* Testez minutieusement en mode brouillon
* Validez toutes les connexions API
* Examinez attentivement les conditions de déclenchement
* Documentez le comportement attendu
### Pendant le développement
* Utilisez des noms d'étapes descriptifs
* Ajoutez des commentaires dans les actions Code
* Testez avec des données réalistes
* Prévoyez les cas limites
### Après l'activation
* Surveillez de près les premières exécutions
* Configurez des alertes pour les échecs
* Examinez régulièrement l'historique des exécutions
* Gardez les flux de travail simples lorsque c'est possible

View File

@@ -0,0 +1,236 @@
---
title: FAQ sur les flux de travail
description: Foire aux questions sur les flux de travail dans Twenty.
---
<AccordionGroup>
<Accordion title="Pourquoi ne puis-je pas activer un flux de travail ?">
Il s'agit probablement d'un problème de permissions. Vous avez besoin d'un accès aux flux de travail pour les créer et les activer.
**Solution** : Contactez l'administrateur de votre espace de travail pour vous accorder l'accès aux flux de travail dans **Paramètres → Membres → Rôles**.
Si vous ne voyez pas du tout la section des flux de travail dans votre barre latérale, cela confirme qu'il s'agit d'un problème de permissions.
</Accordion>
<Accordion title="Pourquoi ne vois-je pas mon bouton de flux de travail manuel dans la barre de navigation supérieure ?">
Les flux de travail manuels n'apparaissent dans la barre de navigation que s'ils sont correctement configurés :
1. Le flux de travail doit être **activé** (pas en mode brouillon)
2. L'emplacement dans la barre de navigation doit être défini sur **Pinned**
3. Pour les déclencheurs Single/Bulk, vous devez vous trouver sur la page d'objet appropriée
**À vérifier** : Ouvrez le flux de travail → cliquez sur le déclencheur → vérifiez que "Navbar placement" est défini sur "Pinned".
Vous pouvez toujours accéder aux flux de travail manuels via **Cmd + K** (ou **Ctrl + K**), quels que soient les paramètres de la barre de navigation.
</Accordion>
<Accordion title="Quelle est la différence entre les déclencheurs manuels Single, Global et Bulk ?">
| Type | Enregistrements requis | Exécutions de workflows |
| ---- | ---------------------- | ----------------------- |
\| **Global** | Aucun | Une fois, aucun enregistrement |
\| **Single** | Un ou plusieurs sélectionnés | Une fois par enregistrement sélectionné |
\| **En bloc** | Un ou plusieurs sélectionnés | Une fois, avec tous les enregistrements en tant que tableau |
* **Global** : À utiliser lorsque le flux de travail n'a besoin d'aucun contexte d'enregistrement (p. ex., générer un rapport)
* **Single** : À utiliser lorsque vous souhaitez traiter chaque enregistrement sélectionné indépendamment (p. ex., envoyer des e-mails individuels)
* **Bulk** : À utiliser lorsque vous devez traiter les enregistrements ensemble ou optimiser l'utilisation des crédits (nécessite l'action Iterator)
Voir [Déclencheurs de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-triggers) pour plus de détails.
</Accordion>
<Accordion title="Je ne vois pas de nœud If/Else. Que dois-je faire ?">
Un nœud If/Else explicite n'est pas encore disponible, mais il figure sur notre feuille de route.
**Solution de contournement actuelle** : Créez plusieurs branches à partir de votre étape, chacune commençant par une action **Filter** :
```
Step 1
│
├── Branch A: Filter (condition = true) → Actions...
│
└── Branch B: Filter (condition = false) → Actions...
```
Seule la branche où la condition du filtre est vérifiée exécutera ses actions suivantes.
Voir [Comment utiliser les branches](/l/fr/user-guide/workflows/capabilities/workflow-branches) pour un guide étape par étape.
</Accordion>
<Accordion title="Les branches de flux de travail s'exécutent-elles en parallèle ?">
**Oui**, les branches s'exécutent par défaut en parallèle.
Si vous souhaitez qu'une seule branche s'exécute :
* Ajoutez une action **Filter** au début de chaque branche
* Définissez des conditions opposées (p. ex., Branche A : status = "Open", Branche B : status ≠ "Open")
Les branches qui ne satisfont pas leur condition de filtre cessent de s'exécuter, tandis que les autres continuent.
</Accordion>
<Accordion title="Puis-je fusionner les branches ?">
**Oui**. Après l'exécution de vos branches parallèles, vous pouvez ajouter une étape à laquelle les deux branches se connectent.
Dans l'éditeur de flux de travail :
1. Terminez vos actions dans les branches
2. Ajoutez une nouvelle étape après les branches
3. Faites glisser des connexions depuis la fin de chaque branche vers cette nouvelle étape
L'étape fusionnée s'exécutera après l'achèvement de toutes les branches connectées.
</Accordion>
<Accordion title="Quelle est la limite des résultats de Search Records ?">
**Search Records renvoie un maximum de 200 enregistrements.**
Si vous devez en traiter davantage :
* Ajoutez des filtres plus spécifiques pour réduire les résultats
* Utilisez des flux de travail planifiés pour traiter par lots
* Envisagez d'utiliser l'API pour des opérations en masse
Pour la plupart des flux de travail, 200 enregistrements suffisent. Si vous atteignez régulièrement cette limite, envisagez de restructurer votre automatisation.
</Accordion>
<Accordion title="Puis-je mettre des personnes en CC sur les e-mails de flux de travail ?">
**Pas encore.** Les champs CC et Cci pour l'action Send Email figurent sur notre feuille de route.
**Solution de contournement actuelle** : Ajoutez plusieurs actions Send Email pour envoyer à des destinataires supplémentaires, ou utilisez une HTTP Request pour envoyer via un service de messagerie externe qui prend en charge le CC.
</Accordion>
<Accordion title="Comment utiliser les sorties des étapes précédentes ?">
Chaque action produit des données de sortie qui peuvent être utilisées dans les étapes suivantes.
**Pour référencer les données d'une étape précédente** :
* Utilisez le sélecteur de variables lors de la configuration d'un champ
* Ou saisissez directement `{{stepName.fieldName}}`
**Exemples** :
* Données du déclencheur : `{{trigger.object.email}}`
* Résultats de la recherche : `{{searchRecords[0].name}}`
* Sortie du code : `{{code.calculatedValue}}`
Survolez n'importe quel champ dans la configuration de l'action pour voir les variables disponibles provenant des étapes précédentes.
</Accordion>
<Accordion title="Pourquoi mon Iterator échoue-t-il ?">
**Iterator nécessite en entrée un tableau.** Problèmes courants :
1. **L'entrée n'est pas un tableau** : Assurez-vous de transmettre les résultats de Search Records ou d'une autre action qui renvoie un tableau
2. **Le tableau est vide** : Ajoutez un filtre avant Iterator pour vérifier `{{searchRecords.length}} > 0`
3. **Mauvaise variable sélectionnée** : Assurez-vous de sélectionner le tableau lui-même, et non un seul enregistrement
**Configuration correcte** :
1. Search Records (renvoie un tableau)
2. Filtre : length > 0
3. Iterator : sélectionnez `{{searchRecords}}`
4. Les actions à l'intérieur d'Iterator utilisent `{{iterator.currentItem.fieldName}}`
</Accordion>
<Accordion title="Quel est le temps d'exécution maximal pour les actions Code ?">
Les actions Code (fonctions logiques) ont un **délai d'expiration par défaut de 5 minutes** (300 secondes).
Le délai d'expiration maximal configurable est de **15 minutes** (900 secondes).
Si votre code dépasse cette limite, l'action échouera avec une erreur de délai d'expiration.
**Conseils pour éviter les délais d'expiration** :
* Divisez les opérations volumineuses en plus petits blocs à l'aide d'Iterator
* Évitez les calculs lourds ; utilisez des services externes via HTTP Request pour les traitements intensifs
* Optimisez votre code pour réduire le temps d'exécution
* Si vous avez besoin d'un traitement plus long, envisagez d'utiliser des flux de travail planifiés qui traitent les données par lots
</Accordion>
<Accordion title="Comment lire les exécutions de flux de travail ?">
Les exécutions de flux de travail affichent l'historique d'exécution et vous aident à diagnostiquer les problèmes.
**Accéder aux exécutions** :
* Dans l'éditeur de flux de travail → volet **Runs** à droite
* Ou allez à **Workflow Runs** dans la barre latérale
**Comprendre une exécution** :
* **Statut** : En cours, Terminé, Échoué, En attente
* **Étapes** : Voyez quelles étapes ont été exécutées et leur sortie
* **Erreurs** : Cliquez sur les étapes en échec pour voir les messages d'erreur
* **Données** : Affichez les données d'entrée/sortie à chaque étape
Voir [Exécutions de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-runs) pour plus de détails.
</Accordion>
<Accordion title="Pourquoi ne vois-je pas mes exécutions de flux de travail ?">
Les exécutions de flux de travail peuvent échouer immédiatement en raison des limites de débit. Vingt impose une limite dure de 5 000 courses par heure par espace de travail.
Si vous dépassez cette limite, les workflows sont immédiatement marqués comme échoués et n'apparaîtront pas dans votre liste d'exécutions.
**Scénarios courants qui atteignent cette limite** :
* Sélection de plus de 5 000 enregistrements avec un déclencheur manuel Single
* Plusieurs flux de travail s'exécutant simultanément dans votre espace de travail
* Déclencheurs automatisés à haute fréquence (p. ex., Record Updated sur un objet très sollicité)
**Solutions** :
* Utilisez des déclencheurs **Bulk** plutôt que Single pour traiter de nombreux enregistrements en une seule exécution
* Espacez les opérations de traitement par lots
* Utilisez des filtres pour réduire la fréquence des déclencheurs
* Planifiez les flux de travail lourds pendant les heures creuses
</Accordion>
<Accordion title="Quelles sont les limites de débit des flux de travail ?">
Twenty a deux limites de débit pour garantir la stabilité du système :
| Limite | Valeur | Comportement |
| ------ | ------ | ------------ |
\| **Limite souple** | 100 exécutions/minute | Les exécutions sont mises en file d'attente avec le statut "Non démarré", puis traitées progressivement |
\| **Limite stricte** | 5 000 exécutions/heure | Les exécutions échouent immédiatement |
**Limite souple (100/min)** : Vos flux de travail n'échoueront pas — ils attendent simplement dans la file et sont traités au fil du temps. Vous pouvez déclencher plus de 100 enregistrements ; l'exécution sera plus lente.
**Limite stricte (5 000/h)** : Cela s'applique à l'ensemble de votre espace de travail. Si l'ensemble de vos flux de travail dépasse 5 000 exécutions en une heure, les exécutions supplémentaires échoueront immédiatement.
**Conseils pour rester dans les limites** :
* Utilisez des déclencheurs Bulk avec Iterator au lieu de déclencheurs Single pour les gros lots
* Regroupez les automatisations liées dans un nombre plus restreint de flux de travail
* Utilisez des flux de travail planifiés pour étaler la charge dans le temps
</Accordion>
<Accordion title="Puis-je relancer une exécution de flux de travail ayant échoué ?">
**Non, il n'existe pas de fonctionnalité de nouvelle tentative automatique pour le moment.**
Si une exécution de flux de travail échoue, vous devrez :
1. Examinez l'erreur dans **Paramètres → Flux de travail → [Votre flux de travail] → Runs**
2. Corrigez le problème (données, configuration ou service externe)
3. Déclenchez manuellement à nouveau le flux de travail sur le ou les enregistrements concernés
**Conseils pour réduire les échecs** :
* Ajoutez des nœuds **Filter** pour valider les données avant les actions
* Utilisez **Search Records** pour vérifier si des enregistrements liés existent
* Testez minutieusement avec quelques enregistrements avant les opérations en masse
La fonctionnalité de nouvelle tentative automatique figure sur notre feuille de route pour une version ultérieure.
</Accordion>
<Accordion title="Dois-je désactiver les flux de travail avant les importations CSV ?">
**Oui, si vos flux de travail sont déclenchés par la création ou la mise à jour d'enregistrements.**
Lorsque vous importez des données via CSV, chaque enregistrement créé ou mis à jour peut déclencher des flux de travail. Un import important (des milliers d'enregistrements) pourrait :
* Atteindre la limite de 5 000 exécutions/heure
* Consommer un nombre important de crédits de flux de travail
* Envoyer des e-mails ou des notifications inattendus
* Créer des tâches ou des enregistrements en double
**Avant un import massif** :
1. Allez dans **Paramètres → Flux de travail**
2. Identifiez les flux de travail déclenchés par l'objet que vous importez
3. **Désactivez**-les temporairement
4. Lancez votre import CSV
5. **Réactivez** les flux de travail une fois terminé
**Alternative** : Si vous avez besoin que les flux de travail s'exécutent sur les données importées, importez par plus petits lots pour rester dans les limites de débit.
</Accordion>
<Accordion title="Comment nettoyer la disposition de mon flux de travail ?">
Si votre canevas de flux de travail semble désordonné avec des nœuds disséminés, vous pouvez l'organiser automatiquement :
1. Cliquez avec le bouton droit n'importe où sur le canevas du flux de travail
2. Cliquez sur **Tidy up workflow**
Cela réorganisera automatiquement tous les nœuds dans une disposition propre et organisée.
</Accordion>
</AccordionGroup>