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,167 @@
---
title: Enviar correos electrónicos desde flujos de trabajo
description: Envía correos electrónicos personalizados automáticamente utilizando acciones de flujos de trabajo.
---
Envía correos electrónicos automáticamente cuando se produzcan eventos específicos en tu CRM—da la bienvenida a nuevos contactos, haz seguimiento de oportunidades o notifica a los miembros del equipo.
## Prerrequisitos
Antes de poder enviar correos electrónicos desde flujos de trabajo:
1. Conecta una cuenta de correo electrónico en **Configuración → Cuentas**
2. Asegúrate de que la cuenta tenga habilitados los permisos de envío
## Flujo de trabajo de correo electrónico básico
### Ejemplo: Correo de bienvenida para nuevos contactos
**Objetivo**: Enviar un correo de bienvenida cuando se agregue una persona nueva al CRM.
**Configuración**:
1. **Crear flujo de trabajo**: Ve a **Configuración → Flujos de trabajo** y haz clic en **+ Nuevo flujo de trabajo**
2. **Agregar disparador**: Selecciona **Se crea el registro** → **Personas**
3. **Agregar la acción Enviar correo electrónico**:
* Haz clic en **+** para agregar una acción
* Selecciona **Enviar correo electrónico**
* Configura el correo electrónico:
| Campo | Valor |
| ---------- | -------------------------------------- |
| **Para** | `{{trigger.object.email}}` |
| **Asunto** | `Bienvenido a {{Your Company Name}}` |
| **Cuerpo** | `Hi {{trigger.object.firstName}}, ...` |
4. **Probar y activar**: Prueba con un registro de ejemplo y luego actívalo
## Uso de variables en correos electrónicos
Haz referencia a datos de pasos anteriores usando la sintaxis `{{variable}}`:
```text
Hi {{trigger.object.firstName}},
Thank you for connecting with us!
Your company, {{trigger.object.company.name}}, is now in our system.
Best regards,
The Team
```
### Variables disponibles de los disparadores
| Tipo de disparador | Variables comunes |
| ------------------------------- | -------------------------------------- |
| **Registro creado/actualizado** | `{{trigger.object.fieldName}}` |
| **Manual** | `{{trigger.selectedRecord.fieldName}}` |
| **Webhook** | `{{trigger.body.fieldName}}` |
## Avanzado: correos electrónicos condicionales
### Ejemplo: Correos diferentes según el origen del lead
**Objetivo**: Enviar correos de bienvenida diferentes según de dónde provenga el lead.
**Configuración**:
1. **Disparador**: Se crea el registro (Personas)
2. **Agregar la acción de filtro**:
* Condición: `{{trigger.object.source}}` es igual a `"Website"`
* Si es verdadero → continúa al correo de bienvenida del sitio web
3. **Rama para otras fuentes**:
* Crea ramas paralelas para diferentes fuentes
* Cada rama tiene su propia acción Enviar correo electrónico
## Envío de correos electrónicos a múltiples destinatarios
### Ejemplo: Notificar al equipo cuando se cierre una oportunidad
**Objetivo**: Enviar un correo al representante de ventas y a su gerente cuando se gane una oportunidad.
**Configuración**:
1. **Disparador**: Se actualiza el registro (Oportunidades, Etapa = "Cerrado Ganado")
2. **Buscar registros**: Busca al gerente del propietario de la oportunidad
3. **Enviar correo electrónico #1**: Al propietario de la oportunidad
* Para: `{{trigger.object.owner.email}}`
* Asunto: `Congratulations on closing {{trigger.object.name}}!`
4. **Enviar correo electrónico #2**: Al gerente
* Para: `{{searchRecords.manager.email}}`
* Asunto: `Deal Won: {{trigger.object.name}}`
## Correos de seguimiento programados
### Ejemplo: Hacer seguimiento 3 días después de la reunión
**Objetivo**: Enviar un correo de seguimiento 3 días después de registrar una reunión.
**Configuración**:
1. **Disparador**: Se crea el registro (Actividades, Tipo = "Reunión")
2. **Acción de retraso**: Espera 3 días
3. **Enviar correo electrónico**:
* Para: Asistente a la reunión
* Asunto: Seguimiento de nuestra conversación
* Cuerpo: Consulta los detalles de la reunión a partir del disparador
## Archivos adjuntos de correo electrónico
Puedes adjuntar archivos a los correos electrónicos enviados desde los flujos de trabajo. El archivo adjunto es un archivo estático que cargas al configurar el flujo de trabajo.
### Cómo añadir un archivo adjunto
1. En la acción **Enviar correo electrónico**, haz clic en el botón **Archivos adjuntos**
2. Haz clic para cargar un archivo desde tu equipo
3. El archivo se adjuntará a todos los correos electrónicos enviados por esta acción
### Casos de Uso Comunes
| Caso de uso | Archivo de ejemplo |
| ------------------------------- | ---------------------------------------- |
| **Correos de bienvenida** | Folleto de la empresa (PDF) |
| **Secuencias de incorporación** | Guía de primeros pasos |
| **Seguimientos de ventas** | Catálogo de productos o lista de precios |
| **Confirmaciones de eventos** | Detalles o agenda del evento |
<Note>
Los archivos adjuntos son estáticos—se envía el mismo archivo a todos los destinatarios. Para documentos dinámicos (como presupuestos personalizados), genera y adjunta archivos usando una [Función lógica](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty).
</Note>
## Mejores prácticas
### Contenido del correo electrónico
* Mantén las líneas de asunto concisas y relevantes
* Personaliza con el nombre del destinatario
* Incluye una llamada a la acción clara
* Prueba los correos antes de activar
### Entregabilidad
* No envíes demasiados correos demasiado rápido
* Usa firmas de correo profesionales
* Evita palabras que activan filtros de spam
* Asegúrate de incluir opciones para darse de baja en los correos de marketing
### Solución de Problemas
* Verifica que la cuenta de correo esté conectada y activa
* Comprueba que la dirección de correo del destinatario sea válida
* Revisa las ejecuciones del flujo de trabajo para ver mensajes de error
* Prueba primero con tu propia dirección de correo electrónico
## Relacionado
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,88 @@
---
title: Usar ramas en flujos de trabajo
description: Comprende cómo funcionan las ramas y cómo controlar qué ruta se ejecuta.
---
## Cómo funcionan las ramas
En el editor de flujos de trabajo, puedes crear varias rutas (ramas) que salgan de un solo nodo. Esto te permite crear automatizaciones complejas con diferentes resultados.
**Importante**: cuando se ejecuta un flujo de trabajo, **todas las ramas se ejecutan en paralelo de forma predeterminada**. No hay una lógica "if/else" incorporada para elegir una rama sobre otra—todas las rutas se ejecutarán simultáneamente.
## Controlar qué rama se ejecuta
Para ejecutar solo una rama según condiciones específicas, **añade un nodo Filter al principio de cada rama**.
### Ejemplo de configuración
1. Crea tu flujo de trabajo con varias ramas desde un único nodo
2. Añade un nodo **Filter** como primer paso en cada rama
3. Configura condiciones en cada **Filter** para determinar cuándo debe continuar esa rama
4. Solo continuarán la(s) rama(s) cuyas condiciones del **Filter** se cumplan
### Cómo funcionan los nodos Filter
* Si se cumple la condición del Filter: la rama sigue ejecutándose
* Si la condición del Filter no se cumple: la rama se detiene en el nodo Filter
Esto crea, en la práctica, una lógica condicional en la que solo se ejecuta la rama adecuada según tus datos.
## Ejemplo: enrutar por tamaño del negocio
**Escenario**: cuando se cierra un negocio, envía distintas notificaciones según el tamaño del negocio.
1. **Disparador**: Oportunidad actualizada (Etapa = Cerrado Ganado)
2. **Rama 1**: Filter para Importe > $10,000 → Enviar mensaje de Slack a #big-deals
3. **Rama 2**: Filter para Importe ≤ $10,000 → Enviar un correo electrónico al gerente de ventas
Ambas ramas se inician, pero solo la que coincida con el importe del negocio continuará más allá de su Filter.
## Creación de ramas
<Note>
Para crear una nueva rama a partir de un paso existente, haz clic en el botón **+** del paso y agrega tu acción. Puedes añadir varias ramas haciendo clic en **+** varias veces.
</Note>
1. En el editor de flujos de trabajo, selecciona el paso desde el que quieres ramificar
2. Haz clic en **+** para agregar una acción
3. Esto crea una rama
4. Haz clic en **+** de nuevo en el mismo paso para crear ramas adicionales
5. Cada rama puede tener su propia secuencia de acciones
## Volver a unir las ramas
Después de que las ramas en paralelo completen su trabajo, puedes volver a unirlas en una sola ruta:
1. Completa tus acciones en las ramas
2. Añade un nuevo paso que deba ejecutarse después de todas las ramas
3. Arrastra conexiones desde el final de cada rama a este nuevo paso
4. El paso fusionado espera a que todas las ramas conectadas se completen antes de ejecutarse
### Ejemplo: procesar y luego notificar
```
Trigger
│
├── Branch A: Update Customer Record
│
└── Branch B: Create Support Ticket
↘ ↙
Merged Step: Send Confirmation Email
```
El correo electrónico de confirmación se envía sólo después de que se hayan completado tanto la actualización del cliente como la creación del ticket.
## Mejores prácticas
* Usa siempre nodos **Filter** al inicio de las ramas cuando quieras una ejecución condicional
* Mantén las condiciones de las ramas mutuamente excluyentes para evitar acciones duplicadas
* Prueba tus flujos de trabajo con distintos datos para asegurarte de que se ejecuten las ramas correctas
* **Renombra los pasos de las ramas** de forma descriptiva para que quede claro qué hace cada ruta
* **Fusiona las ramas** cuando necesites una acción final después del procesamiento en paralelo
## Relacionado
* [Preguntas frecuentes sobre flujos de trabajo](/l/es/user-guide/workflows/how-tos/need-more-help/workflows-faq) — respuestas sobre la ejecución en paralelo
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions) — acciones disponibles para las ramas

View File

@@ -0,0 +1,183 @@
---
title: Usar Iterador
description: Recorre arrays de registros para realizar acciones en cada elemento.
---
El iterador te permite recorrer un array de registros y realizar acciones en cada uno. Es esencial para los flujos de trabajo que necesitan procesar varios registros devueltos por Search Records o recibidos mediante webhooks.
## Cuándo usar el iterador
| Escenario | Ejemplo |
| ----------------------------------- | ------------------------------------------------------ |
| **Procesar resultados de búsqueda** | Enviar un correo electrónico a cada persona encontrada |
| **Gestionar arrays de webhooks** | Crear registros para cada elemento en orden |
| **Actualizaciones masivas** | Actualizar varios registros con valores calculados |
| **Notificaciones** | Alertar a varias personas sobre un evento |
## Comprender el iterador
El iterador espera un **array** como entrada. Luego:
1. Toma el primer elemento del array
2. Ejecuta todas las acciones dentro del iterador con ese elemento
3. Pasa al siguiente elemento
4. Repite hasta que se procesen todos los elementos
### Seleccionar el array sobre el que iterar
La mayoría de las fuentes de arrays (Search Records, un campo de array de un webhook, un Bulk manual trigger) se pueden seleccionar directamente en el selector de variables.
Cuando un paso de **Code** o **Logic Function** devuelve un *array de nivel superior*, su salida aparece en el selector de variables como entradas indexadas (`0`, `1`, `2`, …). Para iterar sobre el array como un todo, selecciona la opción **Whole list** para ese paso; el Iterator entonces infiere automáticamente la estructura de cada elemento a partir de la lista.
## Configuración básica
### Ejemplo: enviar un correo electrónico a todos en los resultados de búsqueda
**Objetivo**: Encontrar todos los contactos de una empresa específica y enviar a cada uno un correo electrónico personalizado.
### Paso 1: Buscar registros
1. Añade la acción **Search Records**
2. Objeto: **People**
3. Filtro: Company es igual a "Acme Inc"
4. Esto devuelve un array de personas
### Paso 2: Comprobar que existan resultados
1. Añade la acción **Filter**
2. Condición: `{{searchRecords.length}}` es mayor que 0
3. Esto evita errores del iterador con resultados vacíos
### Paso 3: Añadir un iterador
1. Añade la acción **Iterator**
2. Entrada del array: selecciona `{{searchRecords}}`
3. Esto crea un bucle
### Paso 4: Añadir acciones dentro del iterador
Las acciones colocadas después del iterador se ejecutan para cada elemento:
1. Añadir acción **Enviar correo electrónico** (dentro del Iterador)
2. Para: `{{iterator.currentItem.email}}`
3. Asunto: Hola `{{iterator.currentItem.firstName}}`!
4. Cuerpo: Mensaje personalizado utilizando los campos del elemento actual
### Resultado
Si Search Records devuelve 5 personas, el iterador:
* Envía un correo electrónico a la persona 1
* Envía un correo electrónico a la persona 2
* ... continúa con las 5
## Acceder a los datos del elemento actual
Dentro del iterador, usa `{{iterator.currentItem}}` para acceder al registro actual:
| Variable | Descripción |
| --------------------------------------- | --------------------------------------------- |
| `{{iterator.currentItem}}` | Todo el objeto de registro actual |
| `{{iterator.currentItem.id}}` | ID de registro |
| `{{iterator.currentItem.email}}` | Campo de correo electrónico |
| `{{iterator.currentItem.company.name}}` | Nombre de la empresa relacionada |
| `{{iterator.index}}` | Posición actual en el array (comenzando en 0) |
En el selector de variables, puedes profundizar en un campo específico del elemento actual, o elegir **Use the whole item** para hacer referencia a todo el elemento actual (`{{iterator.currentItem}}`). Seleccionar el elemento completo es útil cuando quieres pasar un registro completo directamente a un paso posterior en lugar de reconstruirlo campo por campo.
## Patrones comunes
### Actualizar varios registros
**Objetivo**: Marcar todas las tareas atrasadas como "Late"
```
1. Search Records (Tasks, Due Date < Today, Status ≠ Completed)
2. Filter (length > 0)
3. Iterator (searchRecords)
└── Update Record
- Object: Tasks
- Record: {{iterator.currentItem.id}}
- Status: Late
```
### Crear registros desde un array
**Objetivo**: El webhook recibe un pedido con varios artículos, crear un registro para cada uno
```
1. Webhook Trigger (receives items array)
2. Filter (items.length > 0)
3. Iterator (trigger.body.items)
└── Create Record
- Object: Order Items
- Name: {{iterator.currentItem.name}}
- Quantity: {{iterator.currentItem.qty}}
- Related Order: {{trigger.body.orderId}}
```
### Procesamiento condicional dentro del bucle
**Objetivo**: Enviar correo electrónico solo a los contactos con correos válidos
```
1. Search Records (People)
2. Iterator (searchRecords)
└── Filter (currentItem.email is not empty)
└── Send Email
- To: {{iterator.currentItem.email}}
```
## Solución de Problemas
### "El iterador espera un array"
**Causa**: Pasaste un único registro en lugar de un array.
**Solución**: Asegúrate de pasar el resultado de Search Records o un campo de tipo array, no un único registro.
```
✅ Correct: {{searchRecords}}
❌ Wrong: {{searchRecords[0]}}
```
### El iterador no se ejecuta
**Causa**: El array está vacío.
**Solución**: Añade un Filter antes del iterador para comprobar la longitud del array:
```
Filter: {{searchRecords.length}} > 0
```
### Las acciones se ejecutan demasiadas veces
**Causa**: Search Records devolvió más registros de los esperados.
**Solución**:
* Añade filtros más específicos a Search Records
* Establece un límite en Search Records (máx. 200)
* Añade un Filter dentro del iterador para condiciones adicionales
## Consideraciones de rendimiento
* **Uso de créditos**: Cada iteración consume créditos por sus acciones
* **Tiempo**: Los arrays grandes tardan más en procesarse
* **Límites**: Considera procesar por lotes las operaciones muy grandes
* **Límites de velocidad**: Las llamadas a API externas pueden alcanzar límites de velocidad con muchas iteraciones
## Mejores prácticas
1. **Comprueba siempre la longitud del array** antes del iterador para evitar errores
2. **Añade filtros dentro de los bucles** cuando no sea necesario procesar todos los elementos
3. **Renombra tu paso del iterador** para describir qué está recorriendo
4. **Prueba con arrays pequeños** antes de procesar conjuntos de datos grandes
5. **Supervisa las ejecuciones de flujos de trabajo** para asegurarte de que las iteraciones se completen como se espera
## Relacionado
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Cómo usar ramas](/l/es/user-guide/workflows/capabilities/use-branches-in-workflows)
* [Preguntas frecuentes sobre flujos de trabajo](/l/es/user-guide/workflows/how-tos/need-more-help/workflows-faq)

View File

@@ -0,0 +1,332 @@
---
title: Acciones del Flujo de Trabajo
description: Conoce las acciones disponibles en los flujos de trabajo de Twenty.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## Acerca de las acciones
Las acciones definen lo que ocurre después de que se dispara un activador. Puedes encadenar varias acciones para crear automatizaciones complejas.
<Tip>
* Usa el selector de variables (haz clic en el icono `(x+)`) para explorar los datos disponibles de pasos anteriores
* Pasa el cursor sobre cualquier campo de entrada para ver de qué paso proviene una variable — útil cuando el mismo campo (p. ej., ID) existe en varios pasos anteriores
* Asigna a cada acción un nombre descriptivo para facilitar el mantenimiento
</Tip>
## Acciones con registros
<img src="/images/user-guide/workflows/workflow_actions.png" style={{width:'100%'}} />
### Crear Registro
Agrega un nuevo registro a un objeto seleccionado.
**Configuración**:
* Seleccione el objeto de destino
* Complete los campos obligatorios y opcionales
* Usa datos de pasos anteriores o introduce valores manualmente para completar los campos
**Salida**: Los datos del registro recién creado están disponibles para su uso en pasos posteriores.
### Actualizar Registro
Modifica un registro existente en un objeto seleccionado.
<img src="/images/user-guide/workflows/update_record.png" style={{width:'100%'}} />
**Configuración**:
* Seleccione el objeto de destino
* Elija el registro específico a actualizar.
* Puedes elegir un registro fijo, usando el menú desplegable que muestra todos los registros disponibles.
* O puedes seleccionar el registro de forma dinámica, designando un registro encontrado en un paso anterior, usando el `(x+)`. No puedes buscar el registro en función de diferentes criterios en esta etapa. Si aún no has identificado el registro, añade un paso `Search Record` antes de este paso `Update Record`.
* Seleccione campos a modificar e ingrese nuevos valores
**Salida**: Los datos del registro actualizado están disponibles para su uso en pasos posteriores.
### Eliminar Registro
Elimina un registro de un objeto seleccionado.
**Configuración**:
* Seleccione el objeto de destino
* Elija el registro específico a eliminar
**Salida**: Los datos del registro eliminado permanecen disponibles para su uso en pasos posteriores.
### Buscar Registros
Encuentra registros dentro de un objeto seleccionado usando condiciones de filtro.
**Configuración**:
* Seleccione el objeto a buscar
* Establezca criterios de filtro para restringir resultados
* Configure la clasificación y los límites
**Salida**: Devuelve registros coincidentes que se pueden usar en pasos posteriores.
<Note>
**Límite**: Search Records devuelve un máximo de **200 registros**. Si necesitas procesar más, añade filtros específicos para reducir los resultados o utiliza flujos de trabajo programados para procesar por lotes.
</Note>
**Mejor práctica**: Usa [ramas](/l/es/user-guide/workflows/capabilities/workflow-branches) después de Search Records para manejar escenarios de "encontrado" vs "no encontrado".
### Insertar o actualizar registro
Crea un registro nuevo o actualiza uno existente según criterios de coincidencia. Esto es útil cuando no estás seguro de si un registro ya existe.
<img src="/images/user-guide/workflows/upsert_double_identifier.png" style={{width:'100%'}} />
**Configuración**:
* Seleccione el objeto de destino
* Ten en cuenta qué campos pueden usarse para la coincidencia: email para Personas, dominio para Empresas, ID para cualquier objeto o cualquier campo marcado como Único. Deberás completar al menos uno de los siguientes.
* Completa los valores de los campos. No olvides completar al menos uno de los identificadores únicos.
<Note>
**La coincidencia suele funcionar aún mejor al agregar solo un identificador único.** Por ejemplo, la captura de pantalla a continuación hará coincidir empresas según su dominio. El ID no es estrictamente necesario.
</Note>
<img src="/images/user-guide/workflows/upsert_simple_identifier.png" style={{width:'100%'}} />
* Utilice datos de pasos anteriores para completar los campos
**Cómo funciona**:
1. Busca un registro que coincida con sus criterios
2. Si se encuentra → actualiza el registro existente
3. Si no se encuentra → crea un registro nuevo
**Salida**: Los datos del registro creado o actualizado están disponibles para su uso en pasos posteriores.
## Acciones de flujo
### Iterador
**Itera a través de un array de registros** devueltos por un paso anterior, lo que le permite realizar acciones en cada registro de forma individual.
**Configuración**:
* Seleccione la matriz de registros de un paso anterior (por ejemplo, resultados de registros de búsqueda, de un disparador manual con disponibilidad masiva, de un nodo Code). Cuando un paso de Code o de una función lógica devuelve un array de nivel superior, selecciona la opción **Lista completa** para iterar sobre toda la salida.
* Defina las acciones a realizar en cada registro en el bucle.
<Note>
- Puede agregar varias acciones dentro de un Iterador.
- Cuando use ramas dentro de un Iterador, asegúrese de que el último paso de cada rama se conecta de vuelta al Iterador para cerrar el bucle.
</Note>
* Acceda a los campos de `Elemento actual`: para usar campos del registro que se está procesando, haga clic en el paso **Iterador** y luego seleccione **Elemento actual**. Se mostrará la lista de campos disponibles de ese registro y podrá seleccionarlos para usarlos en acciones posteriores. También puedes seleccionar **Usar el elemento completo** para pasar el elemento actual completo a un paso posterior.
<VimeoEmbed videoId="1146577247" title="Demostración en video" />
### Filtro
Filtra registros según condiciones especificadas, permitiendo que solo los registros que cumplan los criterios pasen.
**Configuración**:
* Seleccione el registro a filtrar
* Defina condiciones y criterios de filtro
* Configure qué registros deben pasar a pasos posteriores
<Note>
1. **Salida**: Los nodos de filtro no devuelven datos—actúan como compuertas. Si se cumplen las condiciones, el flujo de trabajo continúa. Si no, el flujo de trabajo se detiene en esa rama.
2. El operador `IS` se puede usar con campos numéricos. Funciona como un `EQUAL`.
</Note>
### Retraso
Pausa la ejecución del flujo de trabajo durante una duración especificada o hasta una fecha/hora específica.
**Tipos de retraso**:
| Tipo | Descripción |
| -------------------- | ------------------------------------------------------------------------ |
| **Duración** | Esperar un período de tiempo específico (días, horas, minutos, segundos) |
| **Fecha programada** | Esperar hasta una fecha y hora específicas |
**Configuración para la duración**:
* Establezca días, horas, minutos y/o segundos
* Combine múltiples unidades (p. ej., 2 días y 4 horas)
**Configuración para la fecha programada**:
* Seleccione una fecha y hora
* Puede hacer referencia a un campo de fecha de un paso anterior (p. ej., hacer seguimiento 3 días después de una reunión)
**Casos de uso**:
* Esperar 24 horas antes de enviar un correo de seguimiento
* Pausar hasta la fecha de cierre de una oportunidad
* Programar acciones para el horario laboral
<Note>
La fecha programada no puede estar en el pasado. Si se usa un campo de fecha de un paso anterior y la fecha ya ha pasado, el flujo de trabajo fallará.
</Note>
**Límites y créditos**:
* **Sin límite máximo de duración**—puede establecer retrasos de minutos, días, semanas o más
* **1 crédito consumido** cuando se ejecuta el nodo Retraso, independientemente de la duración
* **No se consumen créditos** mientras se espera—un retraso de 5 minutos cuesta lo mismo que uno de 5 días
## Acciones de comunicación
### Enviar correo electrónico
Envía un correo electrónico desde su flujo de trabajo. Esto es ideal para correos electrónicos grupales con plantillas. Los correos se verán como los que envía desde su buzón.
No es adecuado para boletines (que requieren un formato más richer) o secuencias de correo electrónico automatizadas.
**Prerrequisitos**: Agregue una cuenta de correo electrónico en Configuración → Cuentas
**Configuración**:
* Seleccione la cuenta de correo del remitente
<Note>
Solo puede enviar correos desde buzones sincronizados con su propia cuenta de Twenty. El envío desde los buzones de otros miembros del equipo (p. ej., el correo del propietario de la cuenta) está en la hoja de ruta.
</Note>
En todos los pasos siguientes, puede hacer referencia a variables de pasos anteriores para la personalización.
* Ingrese la dirección de correo electrónico del destinatario.
<Note>
Por el momento, solo es posible un destinatario.
</Note>
* Establezca la línea de asunto.
* Redacte el cuerpo del mensaje. Puede formatear enlaces, crear listas numeradas, listas de puntos de ballet y añadir archivos adjuntos.
<Note>
No es posible agregar firmas HTML por el momento.
</Note>
### Formulario
Solicita un formulario durante la ejecución del flujo de trabajo para recopilar la entrada del usuario. Las respuestas pueden usarse luego en pasos posteriores para crear registros, enviar correos o ejecutar cualquier otra acción según la entrada.
<Warning>
**Los formularios están diseñados solo para disparadores manuales**. Para flujos de trabajo con otros disparadores (Registro Creado, Actualizado, etc.), los formularios solo son accesibles a través de la interfaz de ejecución de flujos de trabajo, lo cual no es la experiencia de usuario esperada. En 2026 se lanzará un centro de notificación para soportar adecuadamente los formularios en flujos de trabajo automatizados.
</Warning>
**Configuración**:
* Configure los campos que se pedirá a los usuarios que completen. Para cada campo, elija
* un tipo entre texto, número, fecha, un registro dado, un campo de selección. Los campos de todos los objetos están disponibles para su selección.
* una etiqueta
* un valor predeterminado en `Placeholder` (opcional)
* Edite el título del formulario
**Salida**: Las respuestas del formulario están disponibles para su uso en pasos posteriores.
**Ejemplo**: El flujo de trabajo "Quick Lead" está disponible por defecto en todos los espacios de trabajo, disponible en cualquier lugar del menú de comandos `Cmd+K`.
**Cómo completar el formulario**:
* Activa tu flujo de trabajo manual desde el menú de comandos `Cmd+K`
* Complete el formulario que se muestra en el panel lateral y haga clic en `Submit`.
<Note>
Los campos no se pueden hacer obligatorios.
</Note>
<VimeoEmbed videoId="1147277989" title="Demostración en video" />
## Acciones de integración
### Código
Ejecuta JavaScript personalizado dentro de su flujo de trabajo.
Detrás de escena, cada acción de código está respaldada por su propia **función lógica** —una función TypeScript del lado del servidor que se ejecuta en la plataforma Twenty—. Cuando agregas una acción de código, Twenty crea una función lógica dedicada para ese paso y muestra su editor en línea, de modo que el código que escribes queda asociado al flujo de trabajo.
**Configuración**:
* Acceda a variables de pasos anteriores. Puede editar los nombres de las variables dinámicamente.
<VimeoEmbed videoId="1147281795" title="Demostración en video" />
* Escriba el código JavaScript en el editor
* Devuelva variables para su uso en pasos posteriores
* Pruebe el código directamente en el paso
<Note>
Si necesita usar claves de API externas en su código, debe introducirlas directamente en el cuerpo de la función. No puede configurar claves de API en otro lugar y hacer referencia a ellas en la función lógica.
</Note>
<Tip>
**¿Trabaja con arrays?** Los arrays de sistemas externos o de pasos anteriores pueden venir como cadenas. Consulte [Cómo manejar arrays en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions) para la solución.
</Tip>
<Tip>
Haga clic en el icono cuadrado en la parte superior derecha del editor de código para mostrarlo en pantalla completa — es útil, ya que el ancho predeterminado del editor es limitado.
</Tip>
#### Reutilizar una función lógica en distintos flujos de trabajo
La acción de código en línea es ideal para lógica puntual, pero el código que contiene pertenece a ese único paso. Cuando quieras compartir la misma lógica en varios flujos de trabajo —o mantenerla como código fuente versionado en una app—, define una **función lógica reutilizable** en lugar de copiar y pegar código en cada acción de código.
Una función lógica se define una sola vez en una app (usando el `defineLogicFunction` del SDK) y se expone al generador de flujos de trabajo agregando `workflowActionTriggerSettings`. Una vez que tu app está instalada, esa función aparece como su propia acción en el generador de flujos de trabajo, junto con las acciones integradas. Al seleccionarla se muestran los campos de entrada que declaraste (con selectores de variables, como en las demás acciones) y se ejecuta tu código compartido; no se requiere JavaScript en línea.
| | Acción de código | Función lógica reutilizable |
| ------------------------ | -------------------------------------------------------- | -------------------------------------------------------------- |
| **Dónde vive el código** | Editor en línea, vinculado a un paso de flujo de trabajo | Definida en una app, versionada en el control de código fuente |
| **Reutilización** | Copiada y pegada por paso | Agregada como una acción en cualquier flujo de trabajo |
| **Entradas** | Variables conectadas en el editor | Campos mostrados a partir de tu esquema de entrada declarado |
| **Ideal para** | Lógica rápida y específica de un flujo de trabajo | Lógica compartida usada en varios flujos de trabajo |
Ambas se ejecutan en el mismo entorno de ejecución de funciones lógicas, por lo que una acción de código que hayas prototipado en línea puede luego promocionarse a una función lógica reutilizable con cambios mínimos.
<Tip>
Para la referencia orientada a desarrolladores sobre cómo definir funciones lógicas y exponerlas en el generador de flujos de trabajo, consulta [Logic Functions](/l/es/developers/extend/apps/logic/logic-functions), en particular la sección **Exposing a function as an AI tool or workflow action** y `workflowActionTriggerSettings`.
</Tip>
### Solicitud HTTP
Envía una solicitud a un API externo como parte de su flujo de trabajo.
<img src="/images/user-guide/workflows/http_action.png" style={{width:'100%'}} />
**Configuración**:
* Ingrese la URL del extremo de la API. Es posible usar parámetros de pasos anteriores.
* Seleccione el método HTTP (GET, POST, PUT, PATCH, DELETE)
* Agregue encabezados y valores necesarios
* Proporcione una respuesta de muestra para vista previa de estructura
## Acciones de IA
### Agente de IA - Próximamente
Ejecuta un agente de IA dentro de su flujo de trabajo para realizar tareas inteligentes.
**Configuración**:
* **Agente**: Seleccione un agente de IA existente o use el agente predeterminado
* **Prompt**: Escriba la instrucción para el agente de IA
* Haga referencia a variables de pasos anteriores en el prompt
**Qué pueden hacer los agentes de IA**:
* Analizar y resumir datos
* Clasificar o categorizar registros
* Generar contenido de texto
* Tomar decisiones basadas en datos
* Interactuar con sus datos de CRM usando herramientas
**Salida**: La respuesta del agente de IA está disponible para su uso en pasos posteriores. Si el agente tiene un esquema de salida estructurado, la respuesta seguirá ese formato.
<Note>
Las acciones del agente de IA consumen créditos de flujo de trabajo según el modelo de IA utilizado. Consulte [Créditos de flujo de trabajo](/l/es/user-guide/workflows/capabilities/workflow-credits) para obtener detalles.
</Note>
<Note>
Los agentes de IA respetan los permisos basados en roles. Puede asignar roles específicos a los agentes en **Configuración → Miembros → Roles** para controlar a qué datos pueden acceder. Consulte [Permisos](/l/es/user-guide/permissions-access/capabilities/permissions) para obtener detalles.
</Note>

View File

@@ -0,0 +1,66 @@
---
title: Ramas del flujo de trabajo
description: Crea rutas paralelas y lógica condicional en tus flujos de trabajo.
---
Las ramas te permiten dividir tu flujo de trabajo en varias rutas que pueden ejecutarse simultáneamente o de forma condicional según tus datos.
<img src="/images/user-guide/workflows/branches.png" style={{width:'100%'}} />
## Cómo funcionan las ramas
Cuando creas varias conexiones desde un único nodo, cada ruta se convierte en una rama. De forma predeterminada, **todas las ramas se ejecutan en paralelo**—no esperan unas a otras.
## Crear ramas
### Añadir una nueva rama
1. Haz **clic con el botón derecho en el lienzo principal** del flujo de trabajo (no en un nodo existente)
2. Haz clic en **Añadir nodo**
3. Elige el tipo de nodo para tu nueva rama
4. Arrastra una flecha desde la parte inferior del paso anterior hasta la parte superior de esta nueva acción
5. Repite para añadir más ramas desde el mismo nodo
<Note>
Cada rama es independiente. Añadir una rama no afecta a otras rutas existentes desde ese nodo.
</Note>
### Diseño visual
Las ramas aparecen como rutas paralelas en el editor de flujos de trabajo. Puedes arrastrar nodos para reorganizar el diseño visual sin afectar la ejecución.
## Ramas condicionales
Dado que todas las ramas se ejecutan de forma predeterminada, usa nodos **Filter** para controlar qué rutas se ejecutan realmente:
| Rama | Condición del filtro | Acción |
| ---- | --------------------- | ----------------------------- |
| A | Etapa = "Ganada" | Enviar correo de felicitación |
| B | Etapa = "Perdida" | Crear tarea de seguimiento |
| C | Etapa = "Negociación" | Notificar al gerente |
1. Crea ramas desde tu disparador o acción
2. Añade un nodo **Filter** como el primer paso de cada rama
3. Configura cada filtro con condiciones mutuamente excluyentes
4. Añade tus acciones después de cada filtro
Solo continuarán ejecutándose las ramas en las que se cumpla la condición del filtro.
## Combinar ramas
**Las ramas no se combinan automáticamente.** Cada rama se ejecuta de forma independiente hasta que termina. Tienes total flexibilidad para gestionar esto:
* **Opción 1: Mantener las ramas separadas**
Cada rama gestiona sus propias acciones de seguimiento de forma independiente. Este es el enfoque más sencillo cuando las ramas no necesitan converger.
* **Opción 2: Combinar ramas manualmente**
Al crear tu flujo de trabajo, puedes conectar manualmente varias ramas a la misma acción posterior. Simplemente arrastra flechas desde el final de cada rama hasta un nodo común.
<Note>
Aunque puedes usar un nodo [Delay](/l/es/user-guide/workflows/capabilities/workflow-actions#delay) para pausar la ejecución, actualmente no se puede configurar para esperar "hasta que otra rama termine".
</Note>
## Relacionado
* [Cómo usar las ramas en flujos de trabajo](/l/es/user-guide/workflows/capabilities/use-branches-in-workflows) - Guía paso a paso
* [Acciones de flujo de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions) - Acciones disponibles, incluyendo Filter

View File

@@ -0,0 +1,75 @@
---
title: Créditos de Workflow
description: Comprender el consumo y la gestión de los créditos de flujo de trabajo.
---
Los créditos de workflow potencian tus automatizaciones en Twenty. Comprender cómo funcionan te ayuda a optimizar costos y gestionar de manera efectiva tu presupuesto de automatización.
## Asignación de créditos
Los créditos de flujo de trabajo se asignan según tu ciclo de facturación, no según tu nivel de plan:
| Ciclo de facturación | Créditos |
| ----------------------- | ------------------- |
| **Suscripción mensual** | 5 créditos por mes |
| **Suscripción anual** | 50 créditos por año |
<Note>
5 créditos mensuales son generosos para las automatizaciones estándar. La mayoría de los equipos no superarán este límite con un uso típico de flujos de trabajo. Los créditos adicionales se necesitan principalmente para acciones avanzadas de Code y flujos de trabajo impulsados por IA.
</Note>
## Cómo Funciona el Consumo de Créditos
Los créditos se consumen cuando se ejecutan los workflows, no cuando los creas. Cada acción de workflow consume créditos en base a su complejidad:
### Consumo de Créditos por Tipo de Acción
* **Operaciones internas básicas**: Consumo de créditos muy bajo
* Buscar Registros
* Crear Registro
* Actualizar Registro
* Eliminar Registro
* Acciones de formulario
* **Operaciones complejas**: Mayor consumo de créditos
* Acciones de código (ejecución de JavaScript)
* Solicitudes HTTP a servicios externos
* **Funciones de IA**: mayor consumo de créditos
* Las acciones del agente de IA consumen créditos según el modelo de IA utilizado
* Los prompts más complejos y las respuestas más largas usan más créditos
* **Acciones de Delay**: consumo mínimo de créditos
* El nodo Delay consume **1 crédito** cuando se ejecuta
* **No se consumen créditos** durante el período de espera
* Un retraso de 5 minutos cuesta lo mismo que un retraso de 5 días
### Deducción en Tiempo Real
Los créditos se deducen en tiempo real a medida que se ejecutan los workflows. Esto significa:
* Los workflows en borrador no consumen créditos
* Solo los workflows activos y en ejecución utilizan la asignación de créditos
* Los workflows fallidos aún consumen créditos para los pasos completados
## Gestión de Créditos
### Verificar Uso de Créditos
1. Ir a **Ajustes → Facturación**
2. Ve tu consumo actual de créditos y saldo restante
3. Monitorea patrones de uso para optimizar tus workflows
### Adquirir Créditos Adicionales
Si necesitas más créditos adicionales a la asignación de tu plan:
1. Ir a **Ajustes → Facturación**
2. Haz clic en la opción para adquirir créditos adicionales. Hay paquetes de diferentes tamaños disponibles.
3. Los créditos se suman a tu saldo actual
## Mejores prácticas
* **Procesamiento por lote**: Usa operaciones a granel e Iterador de forma eficiente para reducir las llamadas de acción individuales
* **Optimización de disparadores manuales**: Para los disparadores manuales, elige la disponibilidad `Bulk` para procesar varios registros en una sola ejecución de un flujo de trabajo
* Optimiza acciones de Código para eficiencia

View File

@@ -0,0 +1,93 @@
---
title: Ejecuciones de flujos de trabajo
description: Supervise y gestione las ejecuciones de flujos de trabajo.
---
## Acerca de las ejecuciones
Una **Ejecución** es un registro de la ejecución de un flujo de trabajo. Cada vez que se activa un flujo de trabajo—ya sea por un evento de registro, una programación, una acción manual o un webhook—se crea una nueva ejecución.
## Visualización de ejecuciones
### Desde el editor de flujos de trabajo
1. Abra el flujo de trabajo que desea supervisar
2. Haga clic en el panel **Ejecuciones** en el lado derecho
3. Vea una lista de ejecuciones recientes con su estado
### Desde la vista de ejecuciones de flujos de trabajo
1. Vaya a **Ejecuciones de flujos de trabajo** en la barra lateral
2. Vea ejecuciones en todos los flujos de trabajo
3. Filtre por estado, flujo de trabajo o fecha
## Estados de las ejecuciones
| Estado | Descripción |
| ---------------- | ------------------------------------------------------------------------------------------------------- |
| **En ejecución** | El flujo de trabajo se está ejecutando actualmente |
| **Completado** | El flujo de trabajo finalizó correctamente |
| **Fallido** | El flujo de trabajo encontró un error y se detuvo |
| **En espera** | El flujo de trabajo está en pausa (p. ej., esperando una acción de Retardo o el envío de un Formulario) |
## Detalles de la ejecución
Haga clic en cualquier ejecución para ver:
* **Estado**: Estado actual de la ejecución
* **Iniciado**: Momento en que comenzó la ejecución
* **Duración**: Tiempo que tardó la ejecución
* **Datos del disparador**: La entrada que inició el flujo de trabajo
* **Salidas de los pasos**: Datos devueltos por cada paso
* **Mensajes de error**: Si la ejecución falló, qué salió mal
## Ejecución paso a paso
Cada ejecución muestra el progreso a través de su flujo de trabajo:
1. Vea qué pasos se completaron correctamente
2. Identifique dónde se produjeron fallos
3. Vea los datos transferidos entre pasos
4. Depure problemas examinando las entradas y salidas de los pasos
## Gestión de errores
Cuando una ejecución falla:
1. Abra la ejecución fallida
2. Encuentre el paso que causó el fallo
3. Revise el mensaje de error para obtener detalles
Problemas comunes:
* Faltan campos obligatorios
* Formato de datos no válido
* Errores de API externa
* Problemas de permisos
## Volver a ejecutar workflows
Si una ejecución falla, puedes:
* Corregir el problema subyacente y esperar al siguiente disparador
* Para flujos de trabajo manuales, se activa de nuevo con los mismos datos o actualizados
* Revisar la lógica del flujo de trabajo para prevenir futuras fallas
## Consejos de rendimiento
### Gestionar Historial de Ejecución
* Las ejecuciones se mantienen para referencia histórica
* Las ejecuciones muy antiguas pueden ser archivadas automáticamente
* Exportar datos de ejecución si necesita mantener registros
### Monitoreo de Mejores Prácticas
* Comprobar que se ejecuta regularmente después de activar nuevos flujos de trabajo
* Revisar ejecuciones fallidas para identificar patrones
## Relacionado
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Solución de problemas de flujo de trabajo](/l/es/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting)

View File

@@ -0,0 +1,137 @@
---
title: Disparadores de Flujos de Trabajo
description: Obtén información sobre los distintos activadores que inician tus flujos de trabajo.
---
## Acerca de los activadores
Los flujos de trabajo siempre comienzan con un solo disparador que define cuándo debe ejecutarse la automatización.
<img src="/images/user-guide/workflows/workflow_triggers.png" style={{width:'100%'}} />
<Tip>
**¡Se admiten objetos avanzados!** Más allá de los objetos estándar de CRM (Personas, Empresas, Oportunidades), también puedes activar flujos de trabajo y ejecutar acciones sobre:
* Miembros del espacio de trabajo
* Eventos del calendario
* Mensajes (correos electrónicos)
* Tareas, notas y muchos otros objetos del sistema
Esto abre potentes automatizaciones como notificar a los miembros del equipo cuando se crean eventos del calendario o procesar automáticamente los correos electrónicos entrantes.
</Tip>
## Se crea un registro
Inicia el flujo de trabajo cuando se crea un nuevo registro en un objeto seleccionado (Personas, Empresas, Oportunidades o cualquier objeto personalizado).
**Configuración**: Selecciona el tipo de objeto para monitorear nuevos registros.
<Note>
* Este disparador es ideal para registros creados por CSV, buzón de correo y sincronización de calendario, o API.
* **No se recomienda para registros creados manualmente**: con este activador, los flujos de trabajo comienzan tan pronto como se crea el registro. Ya que la interfaz de usuario veinte ofrece guardado automático en la marcha (no hay modo de edición y luego una validación para guardar registros), el flujo de trabajo se activará antes de que el usuario introduzca todos los campos.
Para activar este flujo de trabajo en registros creados manualmente, se recomienda usar el activador `Record is created or updated` en su lugar.
</Note>
## Se actualiza un registro
Inicia el flujo de trabajo cuando se realizan cambios en un registro existente.
**Configuración**:
* Selecciona el tipo de objeto
* Opcionalmente especifica qué campos monitorear para cambios
## Se actualiza o se crea un registro
Inicia el flujo de trabajo cuando un registro es creado o actualizado en un objeto seleccionado.
**¿Por qué esto es importante?**:
* **Importaciones API/CSV**: Los registros se crean con todos los campos poblados inmediatamente
* **Creación manual**: Los registros se crean primero, luego los campos se agregan en actualizaciones posteriores
**Configuración**:
* Selecciona el tipo de objeto para monitorear
* Opcionalmente especifica qué campos monitorear para cambios
* El flujo de trabajo se activará tanto en la creación inicial como en cualquier actualización posterior
## Se elimina un registro
Inicia el flujo de trabajo cuando un registro es eliminado de un objeto.
**Configuración**: Selecciona el tipo de objeto para monitorear eliminaciones.
## Activador manual
Inicia el flujo de trabajo cuando es desencadenado por una acción del usuario. Se puede acceder a este activador a través del menú `Cmd+K` o mediante un botón personalizado que se mostrará en la barra de navegación superior después de seleccionar uno o varios registros.
<img src="/images/user-guide/workflows/add_to_newsletter_button.png" style={{width:'100%'}} />
**Configuración de Disponibilidad**:
Elige cómo debe manejar el flujo de trabajo la selección de registros:
* **Global**: No se requiere ningún registro para activar este flujo de trabajo. El flujo de trabajo se activa desde el menú de comandos `Cmd+K` en cualquier lugar (desde cualquier objeto) y no utiliza registros como entrada.
Para una disponibilidad **única** y **masiva**, los registros seleccionados se pasarán a tu flujo de trabajo. Esto está configurado para un objeto dado, y se pueden seleccionar varios registros antes de activar el flujo de trabajo.
* **Single**: El flujo de trabajo se ejecutará de principio a fin tantas veces como haya registros seleccionados.
<Note>
**Límite flexible: 100 ejecuciones/minuto**. A partir de ese punto, los flujos de trabajo permanecen en estado "Not Started" y se procesan gradualmente—ya sea mediante un trabajo en segundo plano o cuando otro flujo de trabajo entra en la cola. Esto significa que puedes seleccionar más de 100 registros con un único activador; la ejecución solo será más lenta.
</Note>
* **Bulk**: El flujo de trabajo se ejecutará una vez, proporcionando la lista completa de registros como entrada. Esto significa que el flujo de trabajo debe contener una [Acción Iterator](/l/es/user-guide/workflows/capabilities/workflow-actions#iterator).
<Note>
Esto es más avanzado y es ideal para quienes quieren optimizar la cantidad de ejecuciones del flujo de trabajo.
</Note>
<img src="/images/user-guide/workflows/manual_trigger.png" style={{width:'100%'}} />
**Configuración Adicional**:
* Selecciona el objeto de destino (para disponibilidad individual y en masa)
* Elige un ícono de comando para el disparador del flujo de trabajo
* Configura la ubicación en la barra de navegación (fijado o no fijado)
**Métodos de Acceso**:
* Menú `Cmd+K` para encontrar e iniciar flujos de trabajo manuales
* Botón personalizado en la barra de navegación superior (si está configurado)
## Activador basado en tiempo: según un horario
Inicia el flujo de trabajo de forma recurrente según lo defina.
**Configuración**:
* Selecciona la unidad de tiempo (minutos, horas, días)
* Ingresa un valor o usa expresiones cron personalizadas para una programación avanzada
<Note>
**Zona horaria**: Los flujos de trabajo programados se ejecutan en **UTC**. Al configurar las horas para programaciones diarias, convierte tu hora local a UTC.
</Note>
## Activador externo: Webhook
Inicia el flujo de trabajo cuando se recibe una solicitud GET o POST de un servicio externo.
<img src="/images/user-guide/workflows/webhook_trigger.png" style={{width:'100%'}} />
**Configuración**:
* El flujo de trabajo proporciona una URL de webhook única—cópiala y agrégala a tu sistema externo como el endpoint a invocar.
* Para solicitudes POST, define la estructura del cuerpo esperada para que Twenty sepa qué datos esperar. Añade aquí los campos que recibirás y que se necesitarán más abajo en tu flujo de trabajo.
* Configura la autenticación (próximamente).
## Elegir el activador correcto
| Caso de uso | Activador recomendado |
| ------------------------------------------------- | ---------------------------------- |
| Los nuevos leads necesitan procesamiento | Se crea un registro |
| Los cambios en los datos requieren sincronización | Se actualiza un registro |
| Importación/gestión manual de datos | Se actualiza o se crea un registro |
| Limpieza después de la eliminación | Se elimina un registro |
| Acción iniciada por el usuario | Iniciar manualmente |
| Informes recurrentes | Según una programación |
| Integración externa | Webhook o según un horario |

View File

@@ -0,0 +1,84 @@
---
title: Versiones del flujo de trabajo
description: Gestiona versiones y borradores de flujos de trabajo.
---
## Acerca de las versiones
Cada vez que activas un flujo de trabajo, se crea una nueva versión. Esto te permite hacer un seguimiento de los cambios a lo largo del tiempo y revertir a configuraciones anteriores si es necesario.
## Estados de la versión
| Estado | Descripción |
| --------------- | ---------------------------------------------- |
| **Borrador** | En edición, aún no publicado |
| **Activo** | Versión en vivo respondiendo a activadores |
| **Desactivado** | Anteriormente activo pero detenido manualmente |
| **Archivado** | Versiones pasadas guardadas para historial |
## Trabajar con borradores
Cuando editas un flujo de trabajo activo, tus cambios se guardan como un **borrador**. La versión activa sigue ejecutándose mientras trabajas en las actualizaciones.
Cuando termines de editar, puedes:
* **Activar**: Publica el borrador como la nueva versión activa (la versión anterior se archiva)
* **Descartar**: Elimina el borrador y conserva la versión activa actual
## Historial de versiones
### Ver versiones anteriores
1. Abre el flujo de trabajo
2. Haz clic en la pestaña **Versiones**
3. Consulta todas las versiones anteriores con marcas de tiempo
### Restaurar una versión
1. Encuentra la versión que quieres restaurar
2. Haz clic en **Usar como borrador**
3. La versión se copia a un nuevo borrador
4. Realiza las actualizaciones necesarias
5. Activa cuando esté lista
## Mejores prácticas
### Gestión de versiones
* Activa solo cuando esté lista para producción
* Mantén cambios significativos entre versiones
* Documenta los cambios importantes en los nombres o descripciones de los flujos de trabajo
* Prueba en modo borrador antes de activar
### Revertir cambios
* Si una nueva versión causa problemas, restaura la versión anterior
* Usa el historial de versiones para ver qué cambió
* Siempre prueba las versiones restauradas antes de activar
## Flujos de Trabajo comunes
### Edición rápida
1. Realiza cambios menores en un flujo de trabajo activo
2. Prueba en modo borrador
3. Activa la nueva versión
### Revisión importante
1. Usa la versión anterior como punto de partida
2. Realiza cambios significativos en el borrador
3. Prueba a fondo todos los escenarios
4. Activa cuando estés seguro
### Reversión
1. Identifica el problema con la versión actual
2. Encuentra la última versión funcional en el historial
3. Haz clic en **Usar como borrador**
4. Activa para restaurar el comportamiento anterior
## Relacionado
* [Primeros pasos con Flujos de Trabajo](/l/es/user-guide/workflows/overview)
* [Ejecuciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-runs)

View File

@@ -0,0 +1,82 @@
---
title: Manejar arrays en acciones de Code
description: Aprende a manejar correctamente las entradas de arrays en las acciones de Code de flujos de trabajo.
---
Al trabajar con arrays en acciones de Code, puedes encontrarte con dos desafíos comunes:
1. **Arrays pasados como cadenas** — los datos de sistemas externos o pasos anteriores llegan como una cadena en lugar de un array real
2. **No se pueden seleccionar elementos individuales** — solo puedes seleccionar el array completo, no campos específicos dentro de él
Ambos se pueden resolver con un nodo Code.
## Analizar arrays a partir de cadenas
A menudo, los arrays se pasan entre pasos de un flujo de trabajo como cadenas o JSON en lugar de arrays nativos. Esto ocurre cuando:
* Se reciben datos de APIs externas mediante HTTP Request
* Procesar cargas útiles de webhooks
* Pasar datos entre pasos del flujo de trabajo
**Solución**: agrega este patrón al comienzo de tu acción de 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 línea clave `typeof users === "string" ? JSON.parse(users) : users` verifica si la entrada es una cadena, la analiza si es necesario o la usa directamente si ya es un array.
## Extraer campos individuales de arrays
Un webhook podría devolver un array como `answers: [...]`, pero en pasos posteriores del flujo de trabajo solo puedes seleccionar el **array completo**, no elementos individuales dentro de él.
**Solución**: agrega un nodo Code para extraer campos específicos y devolverlos como un objeto estructurado:
```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
}
};
};
```
El nodo Code devuelve un objeto estructurado en lugar de un array. En los pasos posteriores, ahora puedes seleccionar campos individuales como `answer.firstname` y `answer.name` desde el selector de variables.
<Tip>
**¿Quieres iterar sobre el array en lugar de extraer campos?** Cuando un paso de Code o Logic Function devuelve un array de nivel superior, puedes pasarlo directamente a un [Iterator](/l/es/user-guide/workflows/capabilities/use-iterator): selecciona la opción **Whole list** del paso como la entrada del Iterator y luego haz referencia a cada elemento con `{{iterator.currentItem}}` dentro del bucle. En ese caso, no necesitas reestructurar el array en un objeto.
</Tip>
<Tip>
Haz clic en el icono cuadrado en la parte superior derecha del editor de código para mostrarlo en pantalla completa — es útil, ya que el ancho predeterminado del editor es limitado.
</Tip>

View File

@@ -0,0 +1,182 @@
---
title: Incorporar datos de productos en Twenty
description: Sincroniza los datos del catálogo de productos desde un almacén de datos a tu CRM según una programación.
---
Usa este patrón para mantener Twenty sincronizado con los datos de productos de tu almacén de datos (p. ej., Snowflake, BigQuery, PostgreSQL).
## Estructura del flujo de trabajo
1. **Disparador**: Según una programación
2. **Code**: Consultar tu almacén de datos
3. **Code** (opcional): Formatear los datos como array
4. **Iterator**: Recorrer cada producto
5. **Upsert Record**: Crear o actualizar en Twenty
<img src="/images/user-guide/workflows/create_users_workflow.png" style={{width:'100%'}} />
## Paso 1: Programar el disparador
Configura el flujo de trabajo para ejecutarse con una frecuencia acorde a tus necesidades de actualización de datos:
* Cada 5 minutos para una sincronización casi en tiempo real
* Cada hora para datos menos críticos
* Diariamente para actualizaciones por lotes
## Paso 2: Consulta tu almacén de datos
Añade una acción **Code** para obtener datos recientes:
```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>
Filtra por `updated_at >= last X minutes` para recuperar solo los registros cambiados recientemente. Esto mantiene la sincronización eficiente.
</Tip>
## Paso 3: Formatear los datos (opcional)
Si tu almacén devuelve datos en un formato que necesita transformación, añade otra acción **Code**. Las transformaciones comunes incluyen conversiones de tipo, cambio de nombre de campos y limpieza de datos.
### Ejemplo: Datos de usuario con campos booleanos y de estado
```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),
})),
};
};
```
### Ejemplo: Datos de producto con conversiones de tipo
```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
}))
};
};
```
### Ejemplo: Formato de fechas y divisas
```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, " ")
}))
};
};
```
### Transformaciones comunes
| Formato de origen | Formato de destino | Código |
| -------------------- | ------------------ | ---------------------------------------- |
| `"true"` / `"false"` | `true` / `false` | `v === true \|\| v === "true"` |
| `"123.45"` | `123.45` | `parseFloat(value)` |
| `"active"` | `"ACTIVE"` | `value.toUpperCase()` |
| `1704067200` (Unix) | Fecha ISO | `new Date(v * 1000).toISOString()` |
| `"$1,234.56"` | `1234.56` | `parseFloat(v.replace(/[^0-9.-]/g, ""))` |
| `null` / `undefined` | `""` | `value \|\| ""` |
## Paso 4: Recorrer los productos
Añade una acción **Iterator**:
* Entrada: `{{code.products}}`
Esto recorre cada producto del array.
## Paso 5: Insertar o actualizar cada registro
Dentro del iterador, añade una acción **Upsert Record**:
| Configuración | Valor |
| ----------------- | -------------------------------------- |
| **Objeto** | Tu objeto de producto personalizado |
| **Coincidir por** | ID externo o SKU (identificador único) |
| **Nombre** | `{{iterator.item.name}}` |
| **SKU** | `{{iterator.item.sku}}` |
| **Precio** | `{{iterator.item.price}}` |
<Tip>
Usa **Upsert** (actualizar o crear) en lugar de construir ramas separadas para crear frente a actualizar. Es más rápido de construir y más fácil de depurar.
</Tip>
## Casos de uso de ejemplo
| Fuente | Datos |
| -------------------------------------- | --------------------------------------------------- |
| **Sistema ERP** | Catálogo de productos, precios, inventario |
| **Plataforma de comercio electrónico** | Pedidos, clientes, actualizaciones de productos |
| **Almacén de datos** | Métricas agregadas, datos enriquecidos |
| **Sistema de inventario** | Niveles de existencias, alertas de reabastecimiento |
## Relacionado
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Manejar arreglos en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,130 @@
---
title: Importa las respuestas de Typeform a Twenty
description: Gestiona la carga útil del webhook de Typeform para crear clientes potenciales a partir de envíos de formularios.
---
Para la configuración estándar de webhooks, consulta [Configurar un disparador de Webhook](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger). Este artículo cubre el manejo específico requerido para la estructura de carga útil personalizada de Typeform.
### Paso 1: Crear un flujo de trabajo de Webhook
1. Ve a **Configuración → Flujos de trabajo**
2. Haz clic en **+ Nuevo flujo de trabajo**
3. Selecciona **Webhook** como el disparador
4. Copia la URL del webhook
### Paso 2: Configura Typeform
1. En Typeform, abre tu formulario
2. Ve a **Conectar → Webhooks**
3. Pega tu URL de webhook de Twenty
4. Guardar
### Paso 3: Comprender la carga útil de Typeform
Typeform envía una estructura JSON anidada. Aquí hay un ejemplo simplificado:
```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" }
}
]
}
}
```
Aspectos clave a tener en cuenta:
* Los datos del formulario están anidados bajo `form_response`
* **Las respuestas se devuelven como un arreglo**, no como campos con nombre
* Cada respuesta incluye el tipo y el título del campo como referencia
### Paso 4: Extraer campos del arreglo de respuestas
Dado que `answers` es un arreglo, en los pasos posteriores solo puedes seleccionar el arreglo completo — no campos individuales. Añade una acción **Code** para extraer los campos que necesitas:
```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
}
};
};
```
Ahora, en los pasos posteriores, puedes seleccionar `contact.firstName`, `contact.email`, etc., desde el selector de variables.
<Tip>
Para más detalles sobre cómo manejar arreglos en acciones de código, consulta [Cómo manejar arreglos en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions).
</Tip>
### Paso 5: Crear el registro
Añade una acción **Create Record**:
| Campo | Valor |
| ---------------------- | ----------------------------------------------- |
| **Objeto** | Personas |
| **Nombre** | `{{code.contact.firstName}}` |
| **Apellidos** | `{{code.contact.lastName}}` |
| **Correo electrónico** | `{{code.contact.email}}` |
| **Empresa** | Buscar o crear según `{{code.contact.company}}` |
### Paso 6: Probar y activar
1. Envía una respuesta de prueba en Typeform
2. Revisa la ejecución del flujo de trabajo para verificar que se capturaron los datos
3. Activa el flujo de trabajo
## Relacionado
* [Configurar un disparador de Webhook](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger)
* [Manejar arreglos en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,263 @@
---
title: Generar un PDF desde Twenty
description: Cree un flujo de trabajo para generar y adjuntar un PDF (como una cotización) a un registro.
---
Genere u obtenga automáticamente un PDF y adjúntelo a un registro en Twenty. Esto se usa comúnmente para crear cotizaciones, facturas o informes vinculados a Empresas, Oportunidades u otros objetos.
## Resumen
Este flujo de trabajo utiliza un **Activador manual** para que los usuarios puedan generar un PDF bajo demanda para cualquier registro seleccionado. Una **Función de lógica** se encarga de:
1. Descargar el PDF desde una URL (de un servicio de generación de PDF)
2. Subir el archivo a Twenty
3. Crear un archivo adjunto vinculado al registro
## Prerrequisitos
Antes de configurar el flujo de trabajo:
1. **Cree una clave de API**: Vaya a **Settings → APIs** y cree una nueva clave de API. Necesitarás este token para la función lógica.
2. **Configure un servicio de generación de PDF** (opcional): Si desea generar PDFs dinámicamente (p. ej., cotizaciones), use un servicio como Carbone, PDFMonkey o DocuSeal para crear el PDF y obtener una URL de descarga.
## Configuración paso a paso
### Paso 1: Configurar el disparador
1. Vaya a **Flujos de trabajo** y cree un nuevo flujo de trabajo
2. Seleccione **Activador manual**
3. Elija el objeto al que desea adjuntar archivos PDF (p. ej., **Empresa** u **Oportunidad**)
<Tip>
Con un disparador manual, los usuarios pueden ejecutar este flujo de trabajo usando un botón que aparece en la parte superior derecha una vez seleccionado un registro, esto ejecuta el flujo de trabajo para generar y adjuntar un PDF.
</Tip>
### Paso 2: Añadir una Función de lógica
1. Agrega una acción de **código** (función lógica)
2. Cree una nueva función con el siguiente código
3. Configure los parámetros de entrada
#### Parámetros de entrada
| Parámetro | Valor |
| ----------- | ----------------------- |
| `companyId` | `{{trigger.object.id}}` |
<Note>
Si va a adjuntar a un objeto diferente (Persona, Oportunidad, etc.), cambie el nombre del parámetro en consecuencia (p. ej., `personId`, `opportunityId`) y actualice la función de lógica.
</Note>
#### Código de la Función de lógica
```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;
};
```
### Paso 3: Personalice para su caso de uso
#### Para adjuntar a un objeto diferente
Reemplace `targetCompanyId` por el campo correspondiente:
| Objeto | Nombre del campo |
| -------------------- | -------------------------- |
| Empresa | `targetCompanyId` |
| Persona | `targetPersonId` |
| Oportunidad | `targetOpportunityId` |
| Objeto personalizado | `targetYourCustomObjectId` |
Actualice tanto el parámetro de la función como el objeto `variables.data` en la mutación de adjunto.
#### Para usar una URL de PDF dinámica
Si usa un servicio de generación de PDF, puede:
1. Primero, cree una acción de solicitud HTTP para generar el PDF
2. Pase la URL de PDF devuelta a la función de lógica como parámetro
```typescript
export const main = async (
params: { companyId: string; pdfUrl: string; filename: string },
) => {
const { companyId, pdfUrl, filename } = params;
// ... rest of the function
};
```
### Paso 4: Probar y activar
1. Guarde el flujo de trabajo
2. Vaya a un registro de Empresa
3. Haga clic en el menú **⋮** y seleccione su flujo de trabajo
4. Revise la sección **Adjuntos** del registro para verificar que el PDF se haya adjuntado
5. Activa el flujo de trabajo
## Combinación con servicios de generación de PDF
Para crear cotizaciones o facturas dinámicas:
### Ejemplo: Generar cotización → Adjuntar PDF
| Paso | Acción | Propósito |
| ---- | -------------------------- | --------------------------------------------------------------- |
| 1 | Activador manual (Empresa) | El usuario lo inicia en un registro |
| 2 | Buscar registro | Obtener detalles de la oportunidad o de las partidas |
| 3 | Solicitud HTTP | Llamar a la API de generación de PDF con los datos del registro |
| 4 | Función de lógica | Descargar y adjuntar el PDF generado |
### Servicios populares de generación de PDF
* **Carbone** - Generación de documentos basada en plantillas
* **PDFMonkey** - Creación dinámica de PDF a partir de plantillas
* **DocuSeal** - Plataforma de automatización de documentos
* **Documint** - Generación de documentos con enfoque API-first
Cada servicio proporciona una API que devuelve una URL PDF, la cual puede pasar a la función lógica.
## Solución de Problemas
| Problema | Solución |
| --------------------------- | --------------------------------------------------------------------------------------- |
| "Error al descargar el PDF" | Compruebe que la URL del PDF sea accesible y devuelva un PDF válido |
| "Error al subir" | Verifique que su clave de API sea válida y tenga permisos de escritura |
| "Error al crear el adjunto" | Asegúrese de que el nombre del campo de ID del objeto coincida con su objeto de destino |
## Relacionado
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Funciones de lógica](/l/es/user-guide/workflows/capabilities/workflow-actions#code)
* [Generar una cotización o factura desde Twenty](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/generate-quote-or-invoice-from-twenty)

View File

@@ -0,0 +1,144 @@
---
title: Generar una cotización o una factura desde Twenty
description: Crea automáticamente facturas en herramientas externas cuando se ganan las oportunidades.
---
Envía automáticamente los datos de la oportunidad a tu sistema de facturación (Stripe, QuickBooks, Xero, etc.) cuando se gana una oportunidad.
## Estructura del flujo de trabajo
1. **Disparador**: Se actualiza un registro (Oportunidad)
2. **Filtro**: Etapa = Cerrado ganado
3. **Search Record**: Obtener detalles de la empresa
4. **Code** (opcional): Formatear la carga útil
5. **HTTP Request**: Enviar al sistema de facturación
## Paso 1: Configurar el disparador
1. Crear un nuevo flujo de trabajo
2. Selecciona el disparador **Record is Updated**
3. Elige **Opportunity** como objeto
## Paso 2: Filtrar por Cerrado ganado
Añade una acción **Filter** para continuar solo cuando la oportunidad esté ganada:
| Configuración | Valor |
| ------------- | -------------------------------------- |
| **Campo** | Etapa |
| **Condición** | Igual a |
| **Valor** | `CLOSED_WON` (o el nombre de tu etapa) |
<Tip>
El disparador se activa con cualquier actualización de una Oportunidad. El filtro garantiza que el flujo de trabajo solo continúe cuando la etapa cambie a Cerrado ganado.
</Tip>
## Paso 3: Obtener detalles de la empresa
Es posible que el registro de Oportunidad no incluya todos los campos de Empresa que necesitas para la factura. Añade una acción **Search Record**:
| Configuración | Valor |
| ----------------- | ----------------------------------------- |
| **Objeto** | Empresa |
| **Coincidir por** | ID igual a `{{trigger.object.companyId}}` |
Esto recupera el registro completo de Empresa con dirección de facturación, ID fiscal, etc.
## Paso 4: Formatear la carga útil (opcional)
Si tu sistema de facturación exige un formato específico, añade una acción **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
}
}
};
};
```
## Paso 5: Enviar al sistema de facturación
Añade una acción **HTTP Request**:
| Configuración | Valor |
| --------------- | --------------------------------------------------- |
| **Método** | POST |
| **URL** | Tu endpoint de API de facturación |
| **Encabezados** | `Authorization: Bearer YOUR_API_KEY` |
| **Cuerpo** | `{{code.invoice}}` o asigna los campos directamente |
### Ejemplo: Factura de 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
```
### Ejemplo: Factura de QuickBooks
```
POST https://quickbooks.api.intuit.com/v3/company/{realmId}/invoice
Headers:
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Body: {{code.invoice}}
```
## Resumen completo del flujo de trabajo
| Paso | Acción | Propósito |
| ---- | -------------------------------- | --------------------------------------------------------- |
| 1 | Disparador: Registro actualizado | Se activa cuando cambia cualquier Oportunidad |
| 2 | Filtro | Procede solo si la etapa = Cerrado ganado |
| 3 | Search Record | Obtener todos los detalles de Empresa para la facturación |
| 4 | Código | Dar formato a los datos para la API de facturación |
| 5 | Solicitud HTTP | Crear la factura en el sistema externo |
## Consejos
* **Almacenar IDs externos**: Guarda el ID de la factura devuelto por la API en la Oportunidad usando una acción **Update Record**
* **Gestión de errores**: Añade una rama para enviar una notificación si la solicitud HTTP falla
* **Prueba primero**: Utiliza el modo sandbox/pruebas de tu sistema de facturación antes de pasar a producción
## Relacionado
* [Generar un PDF desde Twenty](/l/es/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty) — adjuntar los PDF generados a los registros
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Automatizaciones de Cerrado ganado](/l/es/user-guide/workflows/how-tos/crm-automations/closed-won-automations)

View File

@@ -0,0 +1,170 @@
---
title: Configurar un disparador de webhook
description: Recibe datos de servicios externos para activar flujos de trabajo.
---
Los disparadores de webhook permiten que servicios externos inicien tus flujos de trabajo enviando datos a una URL única. Úsalos para conectar formularios, aplicaciones de terceros e integraciones personalizadas.
## Cuándo usar webhooks
| Caso de uso | Ejemplo |
| -------------------------------- | ---------------------------------------------------------------- |
| **Formularios web** | Los envíos del formulario de contacto crean clientes potenciales |
| **Aplicaciones de terceros** | Pago de Stripe → crear registro de cliente |
| **Integraciones personalizadas** | Tu aplicación → Automatización de Twenty |
| **Herramientas sin código** | Conexiones con Zapier, Make, n8n |
## Configuración paso a paso
### Paso 1: Crear el flujo de trabajo
1. Ve a **Configuración → Flujos de trabajo**
2. Haz clic en **+ Nuevo flujo de trabajo**
3. Ponle un nombre (p. ej., "Envío de formulario del sitio web")
### Paso 2: Configurar el disparador de webhook
1. Haz clic en el bloque del disparador
2. Selecciona **Webhook**
3. Recibirás una URL de webhook única como:
```
https://api.twenty.com/webhooks/workflow/abc123...
```
4. Copia esta URL; la necesitarás para tu servicio externo
### Paso 3: Definir la estructura de datos esperada
Para solicitudes **POST**, define la estructura esperada del cuerpo:
1. Haz clic en **Definir cuerpo esperado**
2. Introduce un JSON de ejemplo que coincida con lo que enviará tu servicio:
```json
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"company": "Acme Inc",
"message": "Interested in your product"
}
```
3. Haz clic en **Guardar**—esto crea variables que puedes usar en pasos posteriores
### Paso 4: Agregar acciones
Ahora agrega acciones que utilicen los datos del webhook:
**Ejemplo: Crear un registro de Persona**
1. Añade la acción **Crear registro**
2. Selecciona el objeto **Personas**
3. Asigna los campos:
| Campo | Valor |
| ------------------ | ----------------------------------------------- |
| Nombre | `{{trigger.body.firstName}}` |
| Apellidos | `{{trigger.body.lastName}}` |
| Correo electrónico | `{{trigger.body.email}}` |
| Empresa | Buscar o crear según `{{trigger.body.company}}` |
### Paso 5: Probar el webhook
Antes de activarlo, prueba tu webhook:
**Usando 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"}'
```
**Usando Postman o similar**:
1. Crea una solicitud POST a tu URL de webhook
2. Establece el encabezado Content-Type en `application/json`
3. Añade tu cuerpo JSON de prueba
4. Envía y verifica las ejecuciones del flujo de trabajo
### Paso 6: Activar
Una vez probado, haz clic en **Activar** para poner el flujo de trabajo en marcha.
## Cómo manejar diferentes estructuras de datos
### Datos anidados
Si tu webhook envía datos anidados:
```json
{
"contact": {
"name": "John Doe",
"email": "john@example.com"
},
"source": "website"
}
```
Haz referencia con: `{{trigger.body.contact.email}}`
### Arrays
Si los datos incluyen arrays:
```json
{
"items": [
{"name": "Product A", "qty": 2},
{"name": "Product B", "qty": 1}
]
}
```
Cómo manejes los arrays depende de tu caso de uso:
**Número desconocido de elementos → Usa Iterator**
Si necesitas procesar cada elemento del array (p. ej., crear un registro para cada uno), añade una acción **Code** para analizar el array y luego usa **Iterator**:
```javascript
export const main = async (params: { items: any }) => {
const items = typeof params.items === "string"
? JSON.parse(params.items)
: params.items;
return { items };
};
```
Luego usa Iterator para iterar: `{{code.items}}`
**Campos conocidos/específicos → Extraer a campos con nombre**
Si el array contiene campos específicos a los que quieres acceder individualmente (p. ej., respuestas de formulario donde la posición 0 siempre es "nombre", la posición 1 siempre es "apellido"), añade una acción **Code** para extraerlos:
```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
}
};
};
```
Ahora puedes seleccionar `product.name` y `product.qty` individualmente en pasos posteriores.
<Tip>
Para más detalles sobre cómo manejar arrays, consulta [Cómo manejar arrays en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions).
</Tip>
## Relacionado
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,121 @@
---
title: Respuesta automática a correos entrantes
description: Crea un flujo de trabajo que utilice IA para clasificar los correos entrantes y enviar respuestas en hilo automáticamente.
---
Responde a los correos entrantes en segundos — no en horas. Este flujo de trabajo utiliza un agente de IA para filtrar el ruido (boletines, spam, respuestas automáticas) y redactar una respuesta personalizada a los mensajes reales. Luego, AI lo envía como una respuesta en hilos dentro de la conversación original.
## Cómo funcionan los hilos de correo electrónico
Cada correo electrónico lleva un encabezado oculto `Message-ID` — un identificador único asignado por el servidor de correo del remitente. Cuando respondes a un correo electrónico, tu cliente de correo establece un encabezado `In-Reply-To` que hace referencia a esa huella. Así es como Gmail, Outlook y cualquier otro cliente agrupan los mensajes en hilos.
En Twenty, esa huella se almacena como `headerMessageId` en el objeto Message. Tu flujo de trabajo la toma y la pasa al campo In-Reply-To de la acción Send Email.
## Crear el flujo de trabajo
### Paso 1: Crear un nuevo flujo de trabajo
Ve a **Settings -> Workflows** y haz clic en **+ New Workflow**.
### Paso 2: Activar con mensajes entrantes
Selecciona **When a Record is Created** y elige **Messages**.
Cada vez que un correo electrónico llega a Twenty, esto se activa.
<img src="/images/user-guide/workflows/auto-reply/workflow-overview.png" style={{width:'100%'}} />
### Paso 3: Buscar quién lo envió
Añade una acción **Search Records**.
La dirección del remitente no está en el propio mensaje — está en el registro relacionado Message Participant.
| Campo | Valor |
| ---------- | ------------------------------- |
| **Objeto** | Participantes del mensaje |
| **Filtro** | Message **es** `{{trigger.id}}` |
| **Filtro** | Role **es** From |
| **Límite** | 1 |
Esto te da el correo electrónico del remitente en `handle` y su nombre en `displayName`.
<img src="/images/user-guide/workflows/auto-reply/find-sender.png" style={{width:'100%'}} />
### Paso 4: Trienal y redactar una respuesta con IA
Añade una acción **AI Agent**. Este único paso hace dos cosas: decide si el correo merece una respuesta y, en ese caso, redacta una.
Usa un prompt como:
```
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.
```
El AI Agent genera su respuesta en un campo `response` que los siguientes pasos pueden consultar.
<img src="/images/user-guide/workflows/auto-reply/ai-triage.png" style={{width:'100%'}} />
### Paso 5: Ramificar según la decisión de la IA
Añade una acción **If/Else** para comprobar si la IA decidió responder u omitir.
| Campo | Valor |
| ------------------- | ------------------------------------------- |
| **Condición** | AI Agent `response` **no contiene** `SKIP` |
| **Si es verdadero** | Continuar con Send Email |
| **Si no** | No hacer nada (el flujo de trabajo termina) |
El spam, los boletines y los mensajes autogenerados se descartan. Todo lo demás pasa al siguiente paso.
<img src="/images/user-guide/workflows/auto-reply/should-reply.png" style={{width:'100%'}} />
### Paso 6: Enviar una respuesta en hilo
Añade una acción **Send Email** en la rama "si es verdadero". Haz clic en **Advanced options**, y luego en **Add In-Reply-To**.
| Campo | Valor |
| --------------- | -------------------------------------- |
| **Para** | `{{Find Sender.first.handle}}` |
| **Asunto** | `Re: {{trigger.subject}}` |
| **Cuerpo** | `{{AI Triage & Draft Reply.response}}` |
| **In-Reply-To** | `{{trigger.headerMessageId}}` |
El campo In-Reply-To es lo que hace que esto sea una respuesta en lugar de una conversación nueva. El destinatario la ve en hilo debajo del correo original en Gmail, Outlook o cualquier otro cliente.
<img src="/images/user-guide/workflows/auto-reply/send-email.png" style={{width:'100%'}} />
<Tip>
**In-Reply-To** espera un `message.headerMessageId` del disparador — es la huella única del correo, no una dirección del destinatario. Si lo dejas vacío, el correo igualmente se envía, solo que como un mensaje independiente.
</Tip>
<Warning>
Gmail usa la línea de asunto para agrupar los mensajes en hilos. El asunto **debe** comenzar con `Re:` (incluidos los dos puntos y el espacio) para que Gmail muestre la respuesta dentro del hilo original. Sin eso, la respuesta aparecerá como una conversación separada — incluso si el encabezado In-Reply-To está configurado correctamente.
</Warning>
### Paso 7: Probar y activar
Haz clic en **Test** y luego revisa tu cliente de correo. La respuesta debería aparecer anidada debajo del mensaje original.
Actívalo cuando estés satisfecho con el resultado.
## Ideas para ampliar
* **Responder solo a VIPs** — añade una rama que compruebe el dominio del remitente o si existe como Contact en Twenty
* **Enrutar por intención** — usa prompts de AI Agent separados para tratar las consultas de ventas de forma diferente a las solicitudes de soporte
* **Enriquecer antes de responder** — añade un paso Search Records para incorporar la empresa del remitente o el historial de oportunidades al prompt de IA para respuestas más personalizadas

View File

@@ -0,0 +1,179 @@
---
title: Automatizaciones de Cerrado Ganado
description: Automatiza las actividades posteriores al cierre cuando las oportunidades se cierran como ganadas.
---
Cuando se cierra un negocio, deben ocurrir varias cosas: actualizar el estado de la empresa, notificar a los miembros del equipo, crear tareas de incorporación. Automatiza todo esto con un único flujo de trabajo.
## Problema
Cuando una oportunidad pasa a "Cerrado ganado":
* El tipo de empresa debe cambiar de "Prospect" a "Customer"
* Se deben crear tareas de incorporación
* Se debe notificar al equipo de Customer Success
* El representante de ventas necesita una confirmación
Hacer esto manualmente consume tiempo y es propenso a errores.
## La solución
Crea un flujo de trabajo que gestione automáticamente todas las actividades posteriores al cierre ganado.
## Configuración completa del flujo de trabajo
### Paso 1: Crear el flujo de trabajo
1. Ve a **Configuración → Flujos de trabajo**
2. Haz clic en **+ Nuevo flujo de trabajo**
3. Asígnale el nombre "Deal Won - Post-Win Automation"
### Paso 2: Configurar el disparador
1. Selecciona **Record is Updated**
2. Elige **Oportunidades**
3. En "Campos a supervisar", selecciona **Etapa**
### Paso 3: Agregar filtro de etapa
1. Añade la acción **Filter**
2. Condición: `{{trigger.object.stage}}` es igual a "Closed Won"
### Paso 4: Actualizar el tipo de empresa
1. Añade la acción **Update Record**
2. Configurar:
| Campo | Valor |
| ---------------------------- | ------------------------------- |
| **Objeto** | Empresas |
| **Registro** | `{{trigger.object.company.id}}` |
| **Tipo** | Cliente |
| **Fecha del primer negocio** | `{{trigger.object.closedAt}}` |
| **Propietario de la cuenta** | `{{trigger.object.owner.id}}` |
### Paso 5: Crear tarea de incorporación
1. Añade la acción **Create Record**
2. Configurar:
| Campo | Valor |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Objeto** | Tareas |
| **Título** | `Incorporación: {{trigger.object.name}}` |
| **Asignado** | Miembro del equipo de Customer Success |
| **Fecha de vencimiento** | Dentro de 3 días |
| **Prioridad** | Alta |
| **Empresa relacionada** | `{{trigger.object.company.id}}` |
| **Oportunidad relacionada** | `{{trigger.object.id}}` |
| **Descripción** | `Incorporación de nuevo cliente para {{trigger.object.company.name}}. Valor del negocio: {{trigger.object.amount}}` |
### Paso 6: Notificar a Customer Success
1. Añade la acción **Send Email**
2. Configurar:
| Campo | Valor |
| ---------- | --------------------------------------------------- |
| **Para** | customer-success@yourcompany.com |
| **Asunto** | `🎉 Nuevo cliente: {{trigger.object.company.name}}` |
| **Cuerpo** | Consulta el ejemplo a continuación |
**Ejemplo de cuerpo del correo**:
```
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!
```
### Paso 7: Confirmar al representante de ventas
1. Añade otra acción **Send Email**
2. Configurar:
| Campo | Valor |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- |
| **Para** | `{{trigger.object.owner.email}}` |
| **Asunto** | `✅ Negocio cerrado: {{trigger.object.name}}` |
| **Cuerpo** | ¡Felicidades! Tu negocio ha sido procesado. Se ha notificado al equipo de Customer Success y la incorporación ha comenzado. |
### Paso 8: Probar y activar
1. Prueba moviendo una oportunidad de prueba a "Closed Won"
2. Verificar:
* El tipo de empresa cambió a "Customer"
* Tarea de incorporación creada
* El equipo de CS recibió el correo electrónico
* El representante de ventas recibió la confirmación
3. Actívalo cuando esté listo
## Cómo gestionar Closed Lost
Crea un flujo de trabajo similar para negocios perdidos:
### Disparador
* Record is Updated (Oportunidades, Etapa = "Closed Lost")
### Acciones
1. **Create Record**: Tarea para "Análisis de negocio perdido"
2. **Update Record**: Agrega el motivo de pérdida al registro de la empresa
3. **Send Email**: Notifica al gerente sobre el negocio perdido
## Avanzado: incorporación de varios pasos
Para una incorporación compleja, crea varias tareas:
```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 };
};
```
Usa **Iterator** para crear cada tarea a partir del array.
## Ideas de personalización
### Mantén tus otras herramientas actualizadas
* Crea el cliente en el sistema de facturación con una **HTTP Request**
### Acciones condicionales
Usa acciones **Filter** para:
* Incorporación diferente para empresas grandes frente a pymes
* Asignados diferentes según la región
* Omitir notificaciones para negocios pequeños
### Incluye detalles de la oferta
Usa la acción **Code** para formatear:
* Documentos de resumen del negocio
* Notas de traspaso para el equipo de CS
* Listas de verificación de incorporación personalizadas
## Relacionado
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Enviar correos electrónicos desde flujos de trabajo](/l/es/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,136 @@
---
title: Detectar oportunidades estancadas
description: Notifica automáticamente a los gerentes cuando las oportunidades no se han actualizado.
---
Mantén tu pipeline saludable informando a los gerentes cuando las oportunidades se estanquen. Este flujo de trabajo revisa las oportunidades que no se han actualizado en un número específico de días.
## Problema
Las oportunidades sin actualizaciones provocan:
* Acuerdos que se enfrían
* Pronósticos poco fiables
* Pérdida de ingresos
## La solución
Crea un flujo de trabajo programado que encuentre oportunidades estancadas y envíe correos electrónicos a sus gerentes.
## Configuración paso a paso
### Paso 1: Crear el flujo de trabajo
1. Ve a **Configuración → Flujos de trabajo**
2. Haz clic en **+ Nuevo flujo de trabajo**
3. Asígnale el nombre "Alerta de oportunidades estancadas"
### Paso 2: Configurar el disparador
1. Selecciona **On a Schedule**
2. Configúralo para ejecutarse diariamente (p. ej., todos los días a las 8 a. m.)
### Paso 3: Buscar oportunidades estancadas
1. Añade la acción **Search Records**
2. Configurar:
| Campo | Valor |
| ---------- | ---------------------------------------------- |
| **Objeto** | Oportunidades |
| **Filtro** | Updated At es anterior a (hoy - 7 días) |
| **Filtro** | Stage no es "Closed Won" Y no es "Closed Lost" |
| **Límite** | 100 |
### Paso 4: Comprobar si se encontró algo
1. Añade la acción **Filter**
2. Condición: `{{searchRecords.length}}` es mayor que 0
3. Si no hay oportunidades estancadas, el flujo de trabajo se detiene aquí
### Paso 5: Formatear la alerta (acción de código)
Añade una acción **Code** para formatear el correo electrónico:
```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
};
};
```
### Paso 6: Enviar correo de alerta
Añade la acción **Send Email**:
| Campo | Valor |
| ---------- | -------------------------------------------------------------------- |
| **Para** | sales-manager@yourcompany.com |
| **Asunto** | `🚨 {{code.totalCount}} oportunidades estancadas requieren atención` |
| **Cuerpo** | `{{code.summary}}` |
### Paso 7: Probar y activar
1. Haz clic en **Test** para ejecutar el flujo de trabajo
2. Verifica que el correo electrónico contenga los datos correctos
3. Actívalo cuando esté listo
## Opciones de personalización
### Cambiar el umbral de estancamiento
Modifica el filtro de Search Records para cambiar de 7 días a tu periodo preferido:
* 3 días para ventas de alto ritmo
* 14 días para acuerdos empresariales
* 30 días para ciclos de ventas largos
### Alertar a los representantes individuales
En lugar de un solo correo para el gerente, usa **Iterator** para enviar correos personalizados a cada representante sobre sus propios acuerdos estancados.
### Agregar escalamiento
Crea varios flujos de trabajo con severidad creciente:
1. Día 7: Correo al representante
2. Día 14: Correo al representante + gerente
3. Día 21: Crear una tarea para que el gerente intervenga
### Incluir en Slack
Usa **HTTP Request** para publicar en un webhook de Slack en lugar del correo electrónico o además de este.
## Relacionado
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Enviar correos electrónicos desde flujos de trabajo](/l/es/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,69 @@
---
title: Mostrar el número de correos electrónicos recibidos
description: Cree un flujo de trabajo para contar y mostrar automáticamente el número de correos electrónicos recibidos de cada contacto.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/workflow-number-of-emails-received.png" style={{width:'100%'}} />
## Resumen
Este flujo de trabajo se activa cada vez que se recibe un nuevo correo electrónico y actualiza un campo personalizado en el registro de Person con el recuento total de correos electrónicos de ese remitente.
## Prerrequisitos
Antes de configurar este flujo de trabajo, cree un campo personalizado en el objeto **People**:
1. Vaya a **Configuración → Modelo de datos → People**
2. Añada un nuevo campo **Number**
3. Asígnele un nombre como "Número de correos electrónicos recibidos de esta persona"
## Configuración paso a paso
<VimeoEmbed videoId="1148700336" title="Demostración en video" />
### Paso 1: Configurar el disparador
1. Vaya a **Flujos de trabajo** y cree un nuevo flujo de trabajo
2. Seleccione **Record is Created** como el disparador
3. Elija **Message Participants** (disponible en objetos avanzados)
<Note>
Un Message Participant es una combinación de un ID de mensaje y un ID de persona, creando un registro único por mensaje. Esto es más fácil de rastrear que Messages directamente porque podemos acceder al campo `handle`, que contiene la dirección de correo electrónico del remitente (o del destinatario).
</Note>
### Paso 2: Filtrar por Rol
1. Añada una acción **Filter**
2. Establezca la condición: **Role** es igual a **FROM**
Esto garantiza que solo cuente los mensajes enviados por esta persona, no los mensajes enviados a esa persona.
### Paso 3: Buscar todos los Message Participants con el mismo Handle
1. Añada una acción **Search Records**
2. Seleccione **Message Participants** como el objeto
3. Añada filtros: **Handle** es igual al handle del disparador (la dirección de correo electrónico del remitente) y **Role** es igual a **FROM**
4. Aumente el **Limit** de 1 a **200** (el máximo)
Esto encuentra todos los mensajes de esta dirección de correo electrónico para obtener el recuento total.
<Note>
La acción Search Records está limitada a devolver un máximo de 200 registros. Sin embargo, dado que solo está utilizando el valor `totalCount` (no los registros individuales), este paso devolverá el número total de correos electrónicos enviados por esta persona.
</Note>
### Paso 4: Actualizar el registro de Person con una acción Create or Update Record
1. Añada una acción **Create or Update Record**
2. Seleccione **People** como el objeto
3. Busque a la persona haciendo coincidir su correo electrónico con el `handle` del Message Participant
4. Establezca su campo personalizado "Número de correos electrónicos recibidos" en `{{searchRecords.totalCount}}`
El valor `totalCount` de la acción Search Records representa el número total de correos electrónicos recibidos de esta persona.
## Relacionado
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Cómo crear campos personalizados](/l/es/user-guide/data-model/how-tos/customize-your-data-model)
* [Acción Search Records](/l/es/user-guide/workflows/capabilities/workflow-actions#search-records)

View File

@@ -0,0 +1,170 @@
---
title: Mostrar datos de registros relacionados
description: Muestra datos de registros relacionados (p. ej., información de la empresa en Oportunidades) usando flujos de trabajo.
---
Muestra datos de registros relacionados directamente en tus registros — por ejemplo, muestra el recuento de empleados de una Empresa en sus Oportunidades. Esta solución provisional con flujos de trabajo es útil hasta que los campos anidados estén disponibles de forma nativa.
## Casos de Uso Comunes
| Fuente | Destino | Campos a copiar |
| ----------- | ----------- | ----------------------------------------------------------------------- |
| Empresa | Oportunidad | Industria, Tamaño de la empresa, ARR |
| Persona | Oportunidad | Correo electrónico, Teléfono, Cargo |
| Oportunidad | Empresa | Importe de la última oportunidad, Fecha de la última oportunidad ganada |
## Copia básica de campos
### Ejemplo: Copiar el correo electrónico del contacto a la oportunidad
**Objetivo**: Al establecer un Punto de contacto en una oportunidad, copia su correo electrónico a la oportunidad para un acceso sencillo.
### Requisito previo
Crea los campos de destino en **Configuración → Modelo de datos → Oportunidades** antes de crear el flujo de trabajo:
* Correo electrónico del contacto (tipo: Email)
* Teléfono del contacto (tipo: Phone)
### Configuración
1. **Desencadenante**: Se actualiza el registro (Oportunidades, campo Punto de contacto)
2. **Filtro**: Comprueba que el Punto de contacto no esté vacío
3. **Buscar registros**: Encuentra a la persona vinculada
* Objeto: Personas
* Filtro: ID igual a `{{trigger.object.pointOfContact.id}}`
4. **Actualizar registro**:
* Objeto: Oportunidades
* Registro: `{{trigger.object.id}}`
* Correo electrónico del contacto: `{{searchRecords[0].email}}`
* Teléfono del contacto: `{{searchRecords[0].phone}}`
## Copiar varios campos
### Ejemplo: Sincronizar la información de la empresa con todas las oportunidades relacionadas
**Objetivo**: Cuando cambien los detalles de la empresa, actualiza todas las oportunidades relacionadas.
### Configuración
1. **Desencadenante**: Se actualiza el registro (Empresas)
* Campos: Industria, Tamaño de la empresa, Ingresos anuales
2. **Buscar registros**: Encuentra todas las oportunidades de esta empresa
* Objeto: Oportunidades
* Filtro: ID de empresa igual a `{{trigger.object.id}}`
3. **Iterator**: Recorre cada oportunidad
4. **Actualizar registro** (dentro del Iterator):
* Objeto: Oportunidades
* Registro: `{{iterator.currentItem.id}}`
* Industria de la empresa: `{{trigger.object.industry}}`
* Tamaño de la empresa: `{{trigger.object.companySize}}`
* ARR de la empresa: `{{trigger.object.annualRevenue}}`
## Copiar al crear el registro
### Ejemplo: Rellenar previamente la oportunidad con datos de la empresa
**Objetivo**: Al crear una oportunidad vinculada a una empresa, copia automáticamente la información clave de la empresa.
### Requisito previo
Crea los campos de destino en **Configuración → Modelo de datos → Oportunidades**:
* Industria de la empresa (tipo: Texto)
* Tamaño de la empresa (tipo: Número)
### Configuración
1. **Desencadenante**: Se crea el registro (Oportunidades)
* Filtro: La empresa no está vacía
2. **Buscar registros**: Obtén los detalles de la empresa vinculada
* Objeto: Empresas
* Filtro: ID igual a `{{trigger.object.company.id}}`
3. **Actualizar registro**:
* Objeto: Oportunidades
* Registro: `{{trigger.object.id}}`
* Industria de la empresa: `{{searchRecords[0].industry}}`
* Tamaño de la empresa: `{{searchRecords[0].employees}}`
<Note>
**Limitación de Tareas y Notas**: Las relaciones en Tareas y Notas están codificadas como de muchos a muchos y aún no están disponibles en los desencadenantes o acciones de flujos de trabajo. Para acceder a estas relaciones, usa la [API](/l/es/developers/extend/api).
</Note>
## Sincronización bidireccional
### Ejemplo: Mantener sincronizado el contacto principal
**Objetivo**: Cuando cambie el contacto principal de una empresa, actualiza el contacto. Cuando una persona se convierta en contacto principal, actualiza la empresa.
### Flujo de trabajo 1: Empresa → Persona
1. **Desencadenante**: Se actualiza el registro (Empresas, campo Contacto principal)
2. **Actualizar registro**: Establece "Es contacto principal" de la persona en true
3. **Buscar registros**: Encuentra el contacto principal anterior
4. **Actualizar registro**: Establece "Es contacto principal" del contacto anterior en false
### Flujo de trabajo 2: Persona → Empresa
1. **Desencadenante**: Se actualiza el registro (Personas, Es contacto principal = true)
2. **Actualizar registro**: Establece el Contacto principal de la empresa en esta persona
<Note>
Ten cuidado con las sincronizaciones bidireccionales para evitar bucles infinitos. Usa filtros para comprobar si el valor realmente cambió antes de actualizar.
</Note>
## Uso de código para mapeo complejo
### Ejemplo: Transformar datos durante la copia
**Objetivo**: Copiar y formatear el número de teléfono de la persona a la oportunidad.
```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 };
};
```
## Mejores prácticas
### Evitar bucles
* No crees flujos de trabajo que se activen mutuamente sin fin
* Usa condiciones específicas de campo
* Añade comprobaciones para ver si el valor realmente cambió
### Gestionar datos faltantes
* Comprueba siempre que el registro de origen exista antes de copiar
* Proporciona valores predeterminados para los campos opcionales
* Usa filtros para omitir cuando el campo de origen esté vacío
### Rendimiento
* Procesa actualizaciones por lotes al copiar a muchos registros
* Usa flujos de trabajo programados para operaciones de sincronización masiva
* Considera usar Iterator para actualizaciones de múltiples registros
## Relacionado
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)

View File

@@ -0,0 +1,202 @@
---
title: Campos de fórmula
description: Crea campos de fórmula usando flujos de trabajo hasta que haya soporte nativo disponible.
---
Veinte todavía no soporta campos de fórmula nativos (llegando en 2026), pero puede lograr el mismo resultado usando flujos de trabajo. Esta solución alternativa te permite calcular y rellenar automáticamente valores de campos—desde concatenaciones simples hasta lógica empresarial compleja.
## Casos de Uso Comunes
| Caso de uso | Ejemplo de fórmula |
| ----------------------------- | ---------------------------------- |
| **Nombre completo** | Nombre + " " + Apellido |
| **Importe esperado** | Importe × Probabilidad |
| **Días hasta el vencimiento** | Fecha de vencimiento - Hoy |
| **Días en la etapa** | Hoy - Fecha de entrada a la etapa |
| **Puntuación del lead** | Puntos basados en varios criterios |
<Tip>
Para ver un ejemplo completo de seguimiento del tiempo en etapas del pipeline, consulta [Cuánto tiempo permanecen las oportunidades en cada etapa](/l/es/user-guide/views-pipelines/how-tos/track-time-in-stage).
</Tip>
## Fórmula básica: concatenación
### Ejemplo: rellenar automáticamente el nombre completo
**Objetivo**: Combinar automáticamente nombre y apellido en un campo de nombre completo.
### Configuración
1. **Desencadenante**: Se actualiza o se crea el registro (Personas)
2. **Filtro**: Comprueba que el nombre o el apellido hayan cambiado
3. **Acción de código**:
```javascript
export const main = async (params) => {
const { firstName, lastName } = params;
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ');
return { fullName };
};
```
4. **Actualizar registro**: Establecer Nombre completo en `{{code.fullName}}`
## Fórmula numérica: importe esperado
### Ejemplo: calcular ingresos esperados
**Objetivo**: Multiplicar el importe de la oportunidad por la probabilidad para obtener el importe esperado.
Consulta [Cómo mostrar el importe esperado en el pipeline](/l/es/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline) para ver el flujo de trabajo completo.
### Configuración
1. **Desencadenante**: Se actualiza el registro (Oportunidades, campo Importe o Probabilidad)
2. **Acción de código**:
```javascript
export const main = async (params) => {
const { amount, probability } = params;
const expectedAmount = (amount || 0) * (probability || 0) / 100;
return { expectedAmount };
};
```
3. **Actualizar registro**: Establecer Importe esperado en `{{code.expectedAmount}}`
## Fórmula de fechas: cálculo de días
### Ejemplo: días hasta el vencimiento de la tarea
**Objetivo**: Calcular cuántos días faltan hasta la fecha de vencimiento de una tarea.
### Configuración
1. **Desencadenante**: Se actualiza o se crea el registro (Tareas, campo Fecha de vencimiento)
2. **Acción de código**:
```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. **Actualizar registro**: Establecer Días hasta el vencimiento en `{{code.daysUntilDue}}`
<Note>
Los valores negativos indican tareas vencidas. Puedes usar este campo para filtrar o ordenar tareas por urgencia.
</Note>
## Fórmula condicional: puntuación del lead
### Ejemplo: calcular la puntuación del lead según criterios
**Objetivo**: Puntuar leads en función del tamaño de la empresa, el sector y la interacción.
### Configuración
1. **Desencadenante**: Se actualiza el registro (Personas o Empresas)
2. **Acción de código**:
```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. **Actualizar registro**: Establecer Puntuación del lead en `{{code.leadScore}}`
## Fórmula de texto: extracción de dominio
### Ejemplo: extraer el dominio del correo electrónico
**Objetivo**: Extraer y almacenar automáticamente el dominio del correo electrónico.
### Configuración
1. **Desencadenante**: Se actualiza el registro (Personas, campo Correo electrónico)
2. **Acción de código**:
```javascript
export const main = async (params) => {
const { email } = params;
if (!email) return { domain: null };
const domain = email.split('@')[1]?.toLowerCase();
return { domain };
};
```
3. **Actualizar registro**: Establecer el campo Dominio en `{{code.domain}}`
## Mejores prácticas
### Rendimiento
* Activa solo ante cambios en campos relevantes
* Usa filtros para omitir registros que no necesiten cálculo
* Evita cálculos complejos en flujos de trabajo de alto volumen
### Manejo de errores
* Comprueba valores null/undefined antes de los cálculos
* Usa valores predeterminados cuando falten datos
* Devuelve mensajes de error claros cuando fallen los cálculos
### Pruebas
* Prueba con casos límite (campos vacíos, valores cero)
* Verifica los cálculos manualmente antes de activar
* Supervisa las ejecuciones de flujos de trabajo para detectar resultados inesperados
## Relacionado
* [Cómo mostrar el importe esperado en el pipeline](/l/es/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline)
* [Cómo realizar el seguimiento del tiempo en la etapa](/l/es/user-guide/views-pipelines/how-tos/track-time-in-stage)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,127 @@
---
title: Notificar a los compañeros de equipo que hay una nota para revisar
description: Enviar notificaciones de correo electrónico automáticas al asignar a un colega para revisar una nota.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<Note>
**Etiquetar directamente a los compañeros de equipo en las notas aún no está disponible.** Esta función está prevista para la segunda mitad de 2026. Mientras tanto, este flujo de trabajo ofrece una solución alternativa práctica.
</Note>
## El desafío
Desea etiquetar a un colega en una nota para que reciba una notificación para revisarla. Actualmente, Twenty no admite @mentions en notas ni tareas.
## La solución alternativa
En lugar de etiquetar en el texto de la nota, puede:
1. Cree un campo de relación **Reviewer** en el objeto Notes
2. Asigne al revisor al crear o editar la nota
3. Haz que un flujo de trabajo envíe automáticamente un correo electrónico al revisor con un enlace a la nota
<Warning>
**Limitación**: Este enfoque permite asignar un revisor por campo. Si necesita notificar a varias personas, cree campos de relación adicionales (p. ej., Reviewer 1, Reviewer 2). Esto no escalará bien si necesita etiquetar a muchos colaboradores a la vez.
</Warning>
## Configuración paso a paso
### Paso 1: Crear el campo Reviewer
<img src="/images/user-guide/workflows/reviewer-field-for-note.png" style={{width:'100%'}} />
1. Vaya a **Configuración → Modelo de datos → Notes**
2. Haga clic en **Add Field**
3. Configurar:
| Configuración | Valor |
| ------------------ | --------------------------------- |
| **Name** | Reviewer |
| **Tipo** | Relación |
| **Related Object** | Miembro del espacio de trabajo |
| **Relation Type** | Many Notes → One Workspace Member |
4. Guarde el campo
### Paso 2: Crear el flujo de trabajo
<VimeoEmbed videoId="1151897168" title="Demostración en video" />
#### Cree el disparador del flujo de trabajo
1. Vaya a la vista principal y cree un nuevo flujo de trabajo
2. Seleccione **Record Updated** como el disparador
3. Elija **Note** como objeto y especifique el campo Reviewer.
4. Esto se activará cada vez que se actualice el revisor de una nota.
#### Agregue un filtro para comprobar que el revisor se acaba de asignar
Añada una acción **Filter** para asegurarse de que el flujo de trabajo solo continúe cuando se asigne un revisor:
| Condición | Valor |
| ------------ | ------------- |
| **Reviewer** | no está vacío |
#### Busque al miembro del espacio de trabajo
1. Añada una acción **Search Records**
2. Seleccione **Workspace Member** (en Advanced Objects)
3. Añada un filtro:
| Condición | Valor |
| --------- | ----------------------------- |
| **ID** | igual a `{{note.reviewerId}}` |
Esto recupera el registro completo del miembro del espacio de trabajo, incluida su dirección de correo electrónico y su nombre.
#### Enviar el correo electrónico de notificación
1. Añada una acción **Send Email**
2. Configurar:
| Campo | Valor |
| ---------- | --------------------------------------------------- |
| **Para** | Search Record → First Workspace Member → User Email |
| **Asunto** | Una nota requiere su revisión |
| **Cuerpo** | Consulte el ejemplo a continuación |
**Ejemplo de cuerpo del correo:**
```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>
Reemplace `yourSubDomain` por el subdominio real de su espacio de trabajo de Twenty.
</Note>
#### Probar y activar
1. Haga clic en **Test** para ejecutar el flujo de trabajo manualmente
2. Cree o edite una nota y asigne un revisor
3. Compruebe la bandeja de entrada del revisor para ver el correo electrónico de notificación
4. Active el flujo de trabajo
## Uso
Una vez configurado, su equipo puede usar este flujo de trabajo de la siguiente manera:
1. Crear una nueva nota (o editar una existente)
2. Seleccionar a un colega en el campo **Reviewer**
3. El colega recibe automáticamente un correo electrónico con un enlace directo a la nota
## Relacionado
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Enviar correos electrónicos desde flujos de trabajo](/l/es/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Crear campos personalizados](/l/es/user-guide/data-model/how-tos/create-custom-fields)
* [Crear campos de relación](/l/es/user-guide/data-model/how-tos/create-relation-fields)

View File

@@ -0,0 +1,106 @@
---
title: Enviar alertas por correo electrónico sobre tareas con fecha de vencimiento
description: Notifica automáticamente a los miembros del equipo sobre sus tareas próximas o vencidas.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/email_tasks_due.png" style={{width:'100%'}} />
Enviar recordatorios diarios por correo electrónico a cada miembro del equipo sobre sus tareas que vencen hoy.
## Resumen
Este flujo de trabajo se ejecuta según una programación y:
1. Obtiene todos los miembros del espacio de trabajo
2. Itera por cada miembro
3. Encuentra sus tareas que vencen hoy
4. Da formato y envía un correo electrónico personalizado
## Configuración paso a paso
<VimeoEmbed videoId="1147463514" title="Demostración en video" />
### Paso 1: Configurar el disparador
1. Ve a **Configuración → Flujos de trabajo** y crea un nuevo flujo de trabajo
2. Selecciona **On a Schedule** como el disparador
3. Usa una expresión cron para ejecutar diariamente a las 8:00 a. m.: `0 8 * * *`
### Paso 2: Buscar a todos los miembros del espacio de trabajo
1. Añade la acción **Search Records**
2. Selecciona **Workspace Members** (en objetos avanzados)
3. No se necesitan filtros — esto devuelve a todos los miembros
### Paso 3: Añadir un iterador
1. Añade la acción **Iterator**
2. Configura el arreglo de entrada con los miembros del espacio de trabajo del paso anterior
3. Todas las acciones dentro del iterador se ejecutarán una vez por miembro
### Paso 4: Buscar tareas que vencen hoy (dentro del iterador)
1. Dentro del iterador, añade una acción **Search Records**
2. Selecciona **Tasks** como el objeto
3. Agregar filtros:
* **Assignee** = miembro actual del espacio de trabajo (del iterador)
* **Due Date** = hoy
### Paso 5: Formatear las tareas para el cuerpo del correo (dentro del iterador)
Añade una acción **Code** para dar formato a las tareas como una lista legible con enlaces:
```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>
Reemplaza `yourSubDomain` por el subdominio real de tu espacio de trabajo de Twenty.
</Note>
### Paso 6: Enviar correo electrónico (dentro del iterador)
1. Añade una acción **Send Email** (aún dentro del iterador)
2. Configurar:
| Campo | Valor |
| ---------- | -------------------------------------------------------------------------------------------- |
| **Para** | `{{iterator.currentItem.userEmail}}` (correo electrónico del miembro del espacio de trabajo) |
| **Asunto** | Tus tareas que vencen hoy |
| **Cuerpo** | `{{code.formattedTasks}}` |
### Paso 7: Probar y activar
1. Haz clic en **Test** para ejecutar el flujo de trabajo manualmente
2. Revisa las bandejas de entrada para ver los correos electrónicos
3. Activa el flujo de trabajo
## Relacionado
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Enviar correos electrónicos desde flujos de trabajo](/l/es/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Manejar arreglos en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,21 @@
---
title: Servicios profesionales
description: Obtenga ayuda profesional para construir flujos de trabajo complejos y automatizaciones del equipo de Twenty y socios certificados.
---
## ¿Cuándo Necesita Ayuda Profesional?
Considere servicios profesionales para:
* Integraciones complejas de múltiples sistemas
* Lógica empresarial avanzada y reglas de automatización
* Flujos de trabajo de procesamiento de datos a gran escala
* Desarrollo de API a medida
* Capacitación del equipo y optimización del flujo de trabajo
* Cuando no tiene recursos internos
## Opciones de Servicio
### Socios de Implementación
Trabaje con socios certificados para personalizaciones avanzadas. Contáctanos en [contact@twenty.com](mailto:contact@twenty.com) para conectarte con nuestros [socios de implementación](https://twenty.com/partners).

View File

@@ -0,0 +1,170 @@
---
title: Resolución de problemas de flujo de trabajo
description: Problemas comunes de flujos de trabajo y cómo resolverlos.
---
## Problemas Comunes y Soluciones
### Flujo de trabajo no se activa
**Síntomas**: Tu flujo de trabajo no se ejecuta cuando lo esperas.
**Posibles causas**:
1. **Flujo de trabajo no activado**: Asegúrate de que el flujo de trabajo esté configurado como "Active", no como "Draft"
2. **Condiciones del disparador no cumplidas**: Verifica que el disparador coincida con el evento esperado
3. **Campo no monitoreado**: Para disparadores "Record is Updated", asegúrate de que se esté monitoreando el campo específico
4. **Permisos**: Verifica que tengas permiso para ejecutar flujos de trabajo
**Soluciones**:
* Verifica el estado del flujo de trabajo en la lista de flujos de trabajo
* Prueba con la acción específica que esperas que lo dispare
* Revisa la configuración del disparador
* Contacta a tu administrador acerca de los permisos
### Los disparadores del flujo de trabajo se activan demasiado pronto (campos vacíos)
**Síntomas**: Al crear un registro manualmente en la IU, tu flujo de trabajo se dispara antes de que tengas tiempo de completar todos los campos. El flujo de trabajo se ejecuta con valores de campo en su mayoría vacíos.
**Por qué sucede esto**: Twenty guarda todo en tiempo real; no hay un modo separado de "editar" y "lectura". Cuando creas un registro, se guarda de inmediato, lo que activa el evento "Record is created" antes de que puedas completar campos adicionales.
**Cuándo funciona bien "Record is created"**:
* Registros creados mediante llamadas de API (los campos se completan en una sola solicitud)
* Registros creados mediante importación
* Creación automática de registros desde otros flujos de trabajo
**Solución**: Para los registros creados manualmente en la IU, usa **"Record is created or updated"** como disparador. De esta forma:
* El flujo de trabajo se dispara después de que el usuario haya terminado de completar y guardar los campos
* Obtienes los datos completos en lugar de valores vacíos
<Tip>
Si solo quieres que el flujo de trabajo se ejecute una vez por registro, agrega una acción de filtro para comprobar un campo como `createdAt equals updatedAt` (primer guardado) o usa un campo de casilla de verificación personalizada para registrar si el flujo de trabajo ya se ejecutó.
</Tip>
### Acciones que fallan
**Síntomas**: El flujo de trabajo se ejecuta, pero algunas acciones fallan.
**Posibles causas**:
1. **Datos faltantes**: Los campos obligatorios están vacíos
2. **Referencias no válidas**: Las variables de pasos anteriores no existen
3. **Errores de API**: Los servicios externos devuelven errores
4. **Problemas de permisos**: La acción requiere permisos que no tienes
**Soluciones**:
* Revisa los detalles de la ejecución del flujo de trabajo para ver mensajes de error
* Verifica que todos los campos obligatorios tengan valores
* Prueba las conexiones de API de forma independiente
* Revisa los permisos del rol
### Errores de solicitudes HTTP
**Síntomas**: Las acciones de solicitud HTTP fallan o devuelven resultados inesperados.
**Códigos de error comunes**:
* **400**: Solicitud incorrecta: verifica el formato del cuerpo de la solicitud
* **401**: No autorizado: verifica la clave de API
* **403**: Prohibido: verifica los permisos de la API
* **404**: No encontrado: verifica la URL del endpoint
* **429**: Demasiadas solicitudes: implementa limitación de tasa
* **500**: Error del servidor: problema del servicio externo
**Soluciones**:
* Verifica la URL del endpoint de la API
* Verifica los encabezados de autenticación
* Primero prueba la llamada a la API fuera de Twenty
* Agrega manejo de errores en las acciones de código
### Errores en acciones de código
**Síntomas**: El código JavaScript no se ejecuta.
**Problemas comunes**:
1. **Errores de sintaxis**: errores tipográficos o JavaScript no válido
2. **Variables indefinidas**: hacer referencia a variables que no existen
3. **Errores de tipo**: operaciones sobre tipos de datos incorrectos
4. **Tiempos de espera agotados**: el código tarda demasiado en ejecutarse
**Soluciones**:
* Usa la validación integrada del editor de código
* Prueba primero la lógica del código en una consola de JavaScript
* Agrega sentencias console.log para depuración
* Simplifica las operaciones complejas
### El correo electrónico no se envía
**Síntomas**: La acción Send Email no entrega correos electrónicos.
**Posibles causas**:
1. **Ninguna cuenta de correo conectada**: verifica Configuración → Cuentas
2. **Dirección de correo electrónico no válida**: el correo electrónico del destinatario está mal formado
3. **Límites de envío**: se alcanzaron los límites de tasa del proveedor de correo electrónico
4. **Filtros de spam**: los correos electrónicos están siendo bloqueados
**Soluciones**:
* Verifica la conexión de la cuenta de correo electrónico
* Valida las direcciones de correo electrónico de los destinatarios
* Verifica los límites del proveedor de correo electrónico
* Revisa el contenido del correo electrónico en busca de desencadenantes de spam
## Depuración de flujos de trabajo
### Uso de las ejecuciones de flujos de trabajo
1. Ve al editor de flujos de trabajo
2. Abre el panel **Runs**
3. Busca la ejecución fallida
4. Haz clic para ver detalles paso a paso
5. Revisa los mensajes de error y los datos de salida
### Prueba de pasos individuales
1. Para las acciones de código, usa el botón **Test**
2. Para las solicitudes HTTP, prueba el endpoint por separado
3. Crea registros de prueba para disparar flujos de trabajo
4. Usa disparadores manuales para pruebas controladas
### Patrones comunes de depuración
**Agrega registros**:
Usa acciones de código para registrar valores intermedios para depuración.
**Aísla pasos**:
Prueba cada paso de forma independiente para identificar fallas.
**Verifica el flujo de datos**:
Comprueba que cada paso reciba los datos de entrada esperados.
## Mejores prácticas para evitar problemas
### Antes de la activación
* Prueba exhaustivamente en modo borrador
* Valida todas las conexiones de API
* Revisa cuidadosamente las condiciones del disparador
* Documenta el comportamiento esperado
### Durante el desarrollo
* Usa nombres de pasos descriptivos
* Agrega comentarios en las acciones de código
* Prueba con datos realistas
* Planifica casos límite
### Después de la activación
* Supervisa de cerca las ejecuciones iniciales
* Configura alertas para fallas
* Revisa el historial de ejecuciones con regularidad
* Mantén los flujos de trabajo simples cuando sea posible

View File

@@ -0,0 +1,236 @@
---
title: Preguntas frecuentes sobre flujos de trabajo
description: Preguntas frecuentes sobre los flujos de trabajo en Twenty.
---
<AccordionGroup>
<Accordion title="¿Por qué no puedo activar un flujo de trabajo?">
Probablemente se deba a un problema de permisos. Necesitas acceso a los flujos de trabajo para crearlos y activarlos.
**Solución**: Ponte en contacto con el administrador de tu espacio de trabajo para que te conceda acceso a flujos de trabajo en **Configuración → Miembros → Roles**.
Si no ves la sección de Flujos de trabajo en la barra lateral, esto confirma que es un problema de permisos.
</Accordion>
<Accordion title="¿Por qué no veo el botón de flujo de trabajo manual en la barra de navegación superior?">
Los flujos de trabajo manuales solo aparecen en la barra de navegación si están configurados correctamente:
1. El flujo de trabajo debe estar **activado** (no en modo borrador)
2. La ubicación en la barra de navegación debe estar configurada como **Pinned**
3. Para los disparadores Single/Bulk, debes estar en la página del objeto correcta
**Para comprobarlo**: Abre el flujo de trabajo → haz clic en el disparador → verifica que "Navbar placement" esté configurado como "Pinned".
Siempre puedes acceder a los flujos de trabajo manuales con **Cmd + K** (o **Ctrl + K**) independientemente de la configuración de la barra de navegación.
</Accordion>
<Accordion title="¿Cuál es la diferencia entre los disparadores manuales Single, Global y Bulk?">
| Tipo | Registros necesarios | Ejecuciones de flujos de trabajo |
| ---- | -------------------- | -------------------------------- |
\| **Global** | Ninguna | Una vez, sin entrada de registro |
\| **Single** | Uno o más seleccionados | Una vez por registro seleccionado |
\| **Bulk** | Uno o más seleccionados | Una vez, con todos los registros como matriz |
* **Global**: Úsalo cuando el flujo de trabajo no necesite ningún contexto de registros (p. ej., generar un informe)
* **Single**: Úsalo cuando quieras procesar cada registro seleccionado de forma independiente (p. ej., enviar correos electrónicos individuales)
* **Bulk**: Úsalo cuando necesites procesar registros en conjunto u optimizar el uso de créditos (requiere la acción Iterator)
Consulta [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers) para más detalles.
</Accordion>
<Accordion title="No veo un nodo If/Else. ¿Qué debo hacer?">
Un nodo If/Else explícito todavía no está disponible, pero está en nuestra hoja de ruta.
**Solución provisional actual**: Crea varias ramas desde tu paso, cada una comenzando con una acción **Filter**:
```
Step 1
│
├── Branch A: Filter (condition = true) → Actions...
│
└── Branch B: Filter (condition = false) → Actions...
```
Solo la rama donde la condición del filtro se cumpla ejecutará sus acciones posteriores.
Consulta [Cómo usar ramas](/l/es/user-guide/workflows/capabilities/workflow-branches) para ver una guía paso a paso.
</Accordion>
<Accordion title="¿Las ramas del flujo de trabajo se ejecutan en paralelo?">
**Sí**, las ramas se ejecutan en paralelo de forma predeterminada.
Si quieres que solo se ejecute una rama:
* Añade una acción **Filter** al inicio de cada rama
* Establece condiciones opuestas (p. ej., Rama A: status = "Open", Rama B: status ≠ "Open")
Las ramas que no cumplan su condición de filtro dejan de ejecutarse, mientras que las demás continúan.
</Accordion>
<Accordion title="¿Puedo volver a unir las ramas?">
**Sí**. Una vez que tus ramas en paralelo hayan terminado, puedes añadir un paso al que se conecten ambas ramas.
En el editor de flujos de trabajo:
1. Completa tus acciones en las ramas
2. Añade un nuevo paso después de las ramas
3. Arrastra conexiones desde el final de cada rama a este nuevo paso
El paso combinado se ejecutará después de que hayan finalizado todas las ramas conectadas.
</Accordion>
<Accordion title="¿Cuál es el límite en los resultados de Search Records?">
**Search Records devuelve un máximo de 200 registros.**
Si necesitas procesar más:
* Añade filtros más específicos para reducir los resultados
* Usa flujos de trabajo programados para procesar por lotes
* Considera usar la API para operaciones masivas
Para la mayoría de los flujos de trabajo, 200 registros son suficientes. Si alcanzas este límite con frecuencia, considera reestructurar tu automatización.
</Accordion>
<Accordion title="¿Puedo poner en CC a personas en los correos electrónicos del flujo de trabajo?">
**Aún no.** Los campos CC y BCC para la acción Send Email están en nuestra hoja de ruta.
**Solución provisional actual**: Añade varias acciones Send Email para enviar a destinatarios adicionales, o usa un HTTP Request para enviar a través de un servicio de correo externo que admita CC.
</Accordion>
<Accordion title="¿Cómo uso las salidas de pasos anteriores?">
Cada acción produce datos de salida que pueden utilizarse en pasos posteriores.
**Para hacer referencia a los datos de un paso anterior**:
* Usa el selector de variables al configurar un campo
* O escribe `{{stepName.fieldName}}` directamente
**Ejemplos**:
* Datos del disparador: `{{trigger.object.email}}`
* Resultados de búsqueda: `{{searchRecords[0].name}}`
* Resultado de código: `{{code.calculatedValue}}`
Pasa el cursor sobre cualquier campo en la configuración de la acción para ver las variables disponibles de pasos anteriores.
</Accordion>
<Accordion title="¿Por qué falla mi Iterator?">
**Iterator requiere una entrada de tipo matriz.** Problemas comunes:
1. **La entrada no es una matriz**: Asegúrate de pasar resultados de Search Records u otra acción que devuelva una matriz
2. **La matriz está vacía**: Añade un filtro antes de Iterator para comprobar `{{searchRecords.length}} > 0`
3. **Variable incorrecta seleccionada**: Asegúrate de seleccionar la propia matriz, no un solo registro
**Configuración correcta**:
1. Search Records (devuelve una matriz)
2. Filter: length > 0
3. Iterator: selecciona `{{searchRecords}}`
4. Las acciones dentro de Iterator usan `{{iterator.currentItem.fieldName}}`
</Accordion>
<Accordion title="¿Cuál es el tiempo máximo de ejecución para las acciones Code?">
Las acciones Code (funciones sin servidor) tienen un **tiempo de espera predeterminado de 5 minutos** (300 segundos).
El tiempo de espera configurable máximo es de **15 minutos** (900 segundos).
Si tu código excede este límite, la acción fallará con un error por tiempo de espera.
**Consejos para evitar tiempos de espera**:
* Divide las operaciones grandes en partes más pequeñas usando Iterator
* Evita cálculos pesados; usa servicios externos mediante HTTP Request para el procesamiento intensivo
* Optimiza tu código para reducir el tiempo de ejecución
* Si necesitas un procesamiento más largo, considera usar flujos de trabajo programados que procesen datos por lotes
</Accordion>
<Accordion title="¿Cómo leo las ejecuciones de flujos de trabajo?">
Las ejecuciones de flujos de trabajo muestran el historial de ejecución y te ayudan a depurar problemas.
**Acceder a las ejecuciones**:
* En el editor de flujos de trabajo → panel **Runs** a la derecha
* O ve a **Workflow Runs** en la barra lateral
**Cómo entender una ejecución**:
* **Estado**: En ejecución, Completada, Fallida, En espera
* **Pasos**: Ve qué pasos se ejecutaron y su salida
* **Errores**: Haz clic en los pasos fallidos para ver los mensajes de error
* **Datos**: Visualiza los datos de entrada/salida en cada paso
Consulta [Ejecuciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-runs) para más detalles.
</Accordion>
<Accordion title="¿Por qué no veo mis ejecuciones de flujos de trabajo?">
Es posible que las ejecuciones de flujos de trabajo estén fallando de inmediato debido a los límites de tasa. Veinte impone un límite duro de 5.000 ejecuciones por hora por espacio de trabajo.
Si excede este límite, los flujos de trabajo se marcan inmediatamente como fallidos y no aparecerán en su lista de ejecuciones.
**Escenarios comunes que alcanzan este límite**:
* Seleccionar más de 5,000 registros con un disparador manual Single
* Múltiples flujos de trabajo ejecutándose simultáneamente en tu espacio de trabajo
* Disparadores automáticos de alta frecuencia (p. ej., Record Updated en un objeto con mucha actividad)
**Soluciones**:
* Usa disparadores **Bulk** en lugar de Single para procesar muchos registros en una sola ejecución
* Espacia las operaciones por lotes grandes
* Usa filtros para reducir la frecuencia de los disparadores
* Programa los flujos de trabajo pesados durante las horas de menor actividad
</Accordion>
<Accordion title="¿Cuáles son los límites de tasa de los flujos de trabajo?">
Twenty tiene dos límites de tasa para garantizar la estabilidad del sistema:
| Límite | Valor | Comportamiento |
| ------ | ----- | -------------- |
\| **Límite suave** | 100 ejecuciones/minuto | Las ejecuciones se encolan con estado "Not Started", se procesan gradualmente |
\| **Límite estricto** | 5,000 ejecuciones/hora | Las ejecuciones fallan de inmediato |
**Límite suave (100/min)**: Tus flujos de trabajo no fallarán; solo esperan en la cola y se procesan con el tiempo. Puedes activar más de 100 registros; la ejecución será más lenta.
**Límite estricto (5,000/h)**: Esto se aplica a todo tu espacio de trabajo. Si todos tus flujos de trabajo combinados superan 5,000 ejecuciones en una hora, las ejecuciones adicionales fallarán de inmediato.
**Consejos para mantenerse dentro de los límites**:
* Usa disparadores Bulk con Iterator en lugar de disparadores Single para lotes grandes
* Combina automatizaciones relacionadas en menos flujos de trabajo
* Usa flujos de trabajo programados para distribuir la carga en el tiempo
</Accordion>
<Accordion title="¿Puedo reintentar una ejecución de flujo de trabajo que falló?">
**No, en este momento no hay funcionalidad de reintento automático.**
Si una ejecución de flujo de trabajo falla, tendrás que:
1. Revisa el error en **Configuración → Flujos de trabajo → [Tu flujo de trabajo] → Ejecuciones**
2. Soluciona el problema (datos, configuración o servicio externo)
3. Activa manualmente el flujo de trabajo de nuevo en los registros afectados
**Consejos para reducir fallos**:
* Añade nodos **Filter** para validar datos antes de las acciones
* Usa **Search Records** para comprobar si existen registros relacionados
* Prueba exhaustivamente con unos pocos registros antes de operaciones masivas
La funcionalidad de reintento automático está en nuestra hoja de ruta para una versión futura.
</Accordion>
<Accordion title="¿Debo desactivar los flujos de trabajo antes de las importaciones CSV?">
**Sí, si tus flujos de trabajo se activan por la creación o actualización de registros.**
Cuando importas datos mediante CSV, cada registro creado o actualizado puede activar flujos de trabajo. Una importación grande (miles de registros) podría:
* Alcanzar el límite de 5,000 ejecuciones/hora
* Consumir una cantidad significativa de créditos de flujos de trabajo
* Enviar correos electrónicos o notificaciones inesperados
* Crear tareas o registros duplicados
**Antes de una importación masiva**:
1. Ve a **Configuración → Flujos de trabajo**
2. Identifica los flujos de trabajo activados por el objeto que estás importando
3. **Desactívalos** temporalmente
4. Ejecuta tu importación CSV
5. **Reactiva** los flujos de trabajo al terminar
**Alternativa**: Si necesitas que los flujos de trabajo se ejecuten con los datos importados, importa en lotes más pequeños para mantenerte dentro de los límites de tasa.
</Accordion>
<Accordion title="¿Cómo limpio el diseño de mi flujo de trabajo?">
Si tu lienzo de flujo de trabajo se ve desordenado con nodos dispersos, puedes organizarlo automáticamente:
1. Haz clic derecho en cualquier parte del lienzo del flujo de trabajo
2. Haz clic en **Tidy up workflow**
Esto reorganizará automáticamente todos los nodos en un diseño limpio y organizado.
</Accordion>
</AccordionGroup>

View File

@@ -0,0 +1,81 @@
---
title: Flujos de trabajo
description: Aprende a crear automatizaciones en Twenty.
---
## Por qué los flujos de trabajo son importantes
Twenty se creó para ofrecer la máxima flexibilidad a sus usuarios. En lugar de obligarte a adaptar tus procesos empresariales a características rígidas y preconstruidas, los workflows te permiten crear automatizaciones que configuran el CRM que mejor respalda los casos de uso únicos de tu negocio.
Workflows es la función integrada de Twenty para desarrollar estas automatizaciones. Te ofrecen los bloques para crear exactamente lo que tu negocio necesita, cuando lo necesita.
## ¿Qué puedo hacer con los workflows?
Recomendamos construir automatizaciones con dos propósitos principales:
1. **Automatizaciones internas para facilitar el día a día de tu equipo**: Reduce la cantidad de entradas manuales y tareas repetitivas que ralentizan a tu equipo.
2. **Incorporar datos dentro y fuera de Twenty**: Conecta Twenty mediante llamadas API y webhooks a tu base de datos y otras herramientas.
## Crear tu primer flujo de trabajo
### Paso 1: Crear un nuevo flujo de trabajo
1. Ve a **Workflows**, accesible debajo de los demás objetos
2. Haz clic en **+ New Record**
3. Asigna un nombre a tu flujo de trabajo
### Paso 2: Agregar un disparador
Todo flujo de trabajo empieza con un disparador. Elige entre:
* **Eventos de registro**: Cuando se crea, se actualiza o se elimina un registro
* **Programación**: Se ejecuta en momentos específicos (diaria, semanal, etc.)
* **Manual**: Activado por una acción del usuario
* **Webhook**: Activado por un webhook
<img src="/images/user-guide/workflows/workflow_triggers.png" style={{width:'100%'}} />
### Paso 3: Agregar acciones
Después de tu disparador, agrega una o más acciones:
* **Crear registro**: Agregar nuevos registros a cualquier objeto
* **Actualizar registro**: Modificar los datos de un registro existente
* **Eliminar registro**: Eliminar registros de los objetos
* **Buscar registros**: Encontrar registros que coincidan con criterios
* **Insertar o actualizar registro**: Crear o actualizar según criterios de coincidencia
* **Iterador**: Recorrer arrays de registros
* **Filtro**: Controlar qué registros continúan
* **Retraso**: Esperar antes de continuar (duración o fecha programada)
* **Enviar correo electrónico**: Enviar correos electrónicos a través de tu cuenta conectada
* **Código**: Ejecutar JavaScript personalizado
* **Solicitud HTTP**: Llamar a APIs externas
* **Formulario**: Obtener datos de los usuarios dentro de la interfaz de Twenty en el momento de la ejecución
* **Agente de IA** (Próximamente): Ejecutar tareas de IA inteligentes
<img src="/images/user-guide/workflows/workflow_actions.png" style={{width:'100%'}} />
### Paso 4: Probar y activar
1. Usa el botón **Test** para ejecutar tu flujo de trabajo con datos de ejemplo
2. Revisa los resultados para asegurarte de que funciona como se espera
3. Cambia el estado del flujo de trabajo a **Active** cuando esté listo
## Mejores prácticas de flujo de trabajo
* **Editar nombres de pasos**: Renombre los pasos de su flujo de trabajo para describir claramente lo que hace cada uno. Esto ayuda con el mantenimiento y facilita la transferencia a compañeros de trabajo
* **Aprovechar los datos de pasos anteriores**: Puede usar campos de registros devueltos por cualquier paso anterior en su flujo de trabajo
* **Comenzar simple**: Inicie con flujos de trabajo básicos y agregue complejidad con el tiempo a medida que se sienta más cómodo con el sistema
* **Planificar antes de construir**: Trace la lógica de su flujo de trabajo antes de comenzar a construir para evitar quedarse atascado a mitad de camino
## Próximos Pasos
* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Automatizaciones de CRM](/l/es/user-guide/workflows/how-tos/crm-automations/closed-won-automations)
## ¿Necesitas Ayuda?
<Tip>
¿Quieres ayuda para automatizar tus flujos de trabajo? [Encuentra un Veinte socio certificado](https://twenty.com/partners/list?categories=SOLUTIONING\&ref=docs-workflows) para diseñar y construir tus automaciones de extremo a extremo.
</Tip>