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,163 @@
---
title: Send Emails from Workflows
description: Send personalized emails automatically using workflow actions.
---
Automatically send emails when specific events occur in your CRM—welcome new contacts, follow up on opportunities, or notify team members.
## Prerequisites
Before you can send emails from workflows:
1. Connect an email account under **Settings → Accounts**
2. Ensure the account has sending permissions enabled
## Basic Email Workflow
### Example: Welcome Email for New Contacts
**Goal**: Send a welcome email when a new person is added to the CRM.
**Setup**:
1. **Create workflow**: Go to **Settings → Workflows** and click **+ New Workflow**
2. **Add trigger**: Select **Record is Created** → **People**
3. **Add Send Email action**:
- Click **+** to add an action
- Select **Send Email**
- Configure the email:
| Field | Value |
|-------|-------|
| **To** | `{{trigger.object.email}}` |
| **Subject** | `Welcome to {{Your Company Name}}` |
| **Body** | `Hi {{trigger.object.firstName}}, ...` |
4. **Test and activate**: Test with a sample record, then activate
## Using Variables in Emails
Reference data from previous steps using `{{variable}}` syntax:
```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
```
### Available Variables from Triggers
| Trigger Type | Common Variables |
|--------------|------------------|
| **Record Created/Updated** | `{{trigger.object.fieldName}}` |
| **Manual** | `{{trigger.selectedRecord.fieldName}}` |
| **Webhook** | `{{trigger.body.fieldName}}` |
## Advanced: Conditional Emails
### Example: Different Emails Based on Lead Source
**Goal**: Send different welcome emails based on where the lead came from.
**Setup**:
1. **Trigger**: Record is Created (People)
2. **Add Filter action**:
- Condition: `{{trigger.object.source}}` equals `"Website"`
- If true → continue to website welcome email
3. **Branch for other sources**:
- Create parallel branches for different sources
- Each branch has its own Send Email action
## Sending Emails to Multiple Recipients
### Example: Notify Team When Deal Closes
**Goal**: Email the sales rep and their manager when an opportunity is won.
**Setup**:
1. **Trigger**: Record is Updated (Opportunities, Stage = "Closed Won")
2. **Search Records**: Find the opportunity owner's manager
3. **Send Email #1**: To opportunity owner
- To: `{{trigger.object.owner.email}}`
- Subject: `Congratulations on closing {{trigger.object.name}}!`
4. **Send Email #2**: To manager
- To: `{{searchRecords.manager.email}}`
- Subject: `Deal Won: {{trigger.object.name}}`
## Scheduled Follow-up Emails
### Example: Follow Up 3 Days After Meeting
**Goal**: Send a follow-up email 3 days after a meeting is logged.
**Setup**:
1. **Trigger**: Record is Created (Activities, Type = "Meeting")
2. **Delay action**: Wait 3 days
3. **Send Email**:
- To: Meeting attendee
- Subject: Following up on our conversation
- Body: Reference meeting details from trigger
## Email Attachments
You can attach files to emails sent from workflows. The attachment is a static file that you upload when configuring the workflow.
### How to Add an Attachment
1. In the **Send Email** action, click on the **Attachments** button
2. Click to upload a file from your computer
3. The file will be attached to all emails sent by this action
### Common Use Cases
| Use Case | Example File |
|----------|--------------|
| **Welcome emails** | Company brochure (PDF) |
| **Onboarding sequences** | Getting started guide |
| **Sales follow-ups** | Product catalog or price list |
| **Event confirmations** | Event details or agenda |
<Note>
Attachments are static—the same file is sent to all recipients. For dynamic documents (like personalized quotes), generate and attach files using a [Logic Function](/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty).
</Note>
## Best Practices
### Email Content
- Keep subject lines concise and relevant
- Personalize with recipient's name
- Include a clear call to action
- Test emails before activating
### Deliverability
- Don't send too many emails too quickly
- Use professional email signatures
- Avoid spam trigger words
- Ensure unsubscribe options for marketing emails
### Troubleshooting
- Verify email account is connected and active
- Check recipient email address is valid
- Review workflow runs for error messages
- Test with your own email address first
## Related
- [Workflow Triggers](/user-guide/workflows/capabilities/workflow-triggers)
- [Workflow Actions](/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,89 @@
---
title: Use Branches in Workflows
description: Understand how branches work and how to control which path is executed.
---
## How Branches Work
In the workflow editor, you can create multiple paths (branches) going out from a single node. This allows you to build complex automations with different outcomes.
**Important**: When a workflow runs, **all branches execute in parallel by default**. There is no built-in "if/else" logic to choose one branch over another—every path will run simultaneously.
## Controlling Which Branch Runs
To execute only one branch based on specific conditions, **add a Filter node at the beginning of each branch**.
### Example Setup
1. Create your workflow with multiple branches from a single node
2. Add a **Filter** node as the first step in each branch
3. Set conditions on each Filter to determine when that branch should continue
4. Only the branch(es) whose Filter conditions are met will proceed
### How Filters Work
- If the Filter condition is **met**: The branch continues executing
- If the Filter condition is **not met**: The branch stops at the Filter node
This effectively creates conditional logic where only the appropriate branch runs based on your data.
## Example: Route by Deal Size
**Scenario**: When a deal is closed, send different notifications based on deal size.
1. **Trigger**: Opportunity updated (Stage = Closed Won)
2. **Branch 1**: Filter for Amount > $10,000 → Send Slack message to #big-deals
3. **Branch 2**: Filter for Amount ≤ $10,000 → Send email to sales manager
Both branches start, but only the one matching the deal amount will continue past its Filter.
## Creating Branches
<Note>
To create a new branch from an existing step, click the **+** button on the step and add your action. You can add multiple branches by clicking **+** multiple times.
</Note>
1. In the workflow editor, select the step you want to branch from
2. Click the **+** button to add an action
3. This creates one branch
4. Click **+** again on the same step to create additional branches
5. Each branch can have its own sequence of actions
## Merging Branches Back Together
After parallel branches complete their work, you can merge them back into a single path:
1. Complete your branched actions
2. Add a new step that should run after all branches
3. Drag a connection from the last step of each branch to this new step
4. The merged step waits for all connected branches to complete before executing
### Example: Process Then Notify
```
Trigger
│
├── Branch A: Update Customer Record
│
└── Branch B: Create Support Ticket
↘ ↙
Merged Step: Send Confirmation Email
```
The confirmation email is sent only after both the customer update and ticket creation are done.
## Best Practices
- Always use **Filter nodes** at the start of branches when you want conditional execution
- Keep branch conditions **mutually exclusive** to avoid duplicate actions
- Test your workflows with different data to ensure the correct branches run
- **Rename branch steps** descriptively so it's clear what each path does
- **Merge branches** when you need a final action after parallel processing
## Related
- [Workflows FAQ](/user-guide/workflows/how-tos/need-more-help/workflows-faq) — answers about parallel execution
- [Workflow Actions](/user-guide/workflows/capabilities/workflow-actions) — available actions for branches

View File

@@ -0,0 +1,181 @@
---
title: Use Iterator
description: Loop through arrays of records to perform actions on each item.
---
Iterator lets you loop through an array of records and perform actions on each one. It's essential for workflows that need to process multiple records returned by Search Records or received via webhooks.
## When to Use Iterator
| Scenario | Example |
|----------|---------|
| **Process search results** | Send email to each person found |
| **Handle webhook arrays** | Create records for each item in order |
| **Bulk updates** | Update multiple records with calculated values |
| **Notifications** | Alert multiple people about an event |
## Understanding Iterator
Iterator expects an **array** as input. It then:
1. Takes the first item from the array
2. Runs all actions inside the Iterator with that item
3. Moves to the next item
4. Repeats until all items are processed
### Selecting the array to loop over
Most array sources (Search Records, a webhook array field, a Bulk manual trigger) can be selected directly in the variable picker.
When a **Code** or **Logic Function** step returns a *top-level array*, its output appears in the variable picker as indexed entries (`0`, `1`, `2`, …). To loop over the array as a whole, select the **Whole list** option for that step — the Iterator then infers the shape of each item from the list automatically.
## Basic Setup
### Example: Email Everyone in Search Results
**Goal**: Find all contacts in a specific company and send each one a personalized email.
### Step 1: Search for Records
1. Add **Search Records** action
2. Object: **People**
3. Filter: Company equals "Acme Inc"
4. This returns an array of people
### Step 2: Check Results Exist
1. Add **Filter** action
2. Condition: `{{searchRecords.length}}` is greater than 0
3. This prevents Iterator errors on empty results
### Step 3: Add Iterator
1. Add **Iterator** action
2. Array input: Select `{{searchRecords}}`
3. This creates a loop
### Step 4: Add Actions Inside Iterator
Actions placed after Iterator run for each item:
1. Add **Send Email** action (inside Iterator)
2. To: `{{iterator.currentItem.email}}`
3. Subject: Hello `{{iterator.currentItem.firstName}}`!
4. Body: Personalized message using current item fields
### Result
If Search Records returns 5 people, the Iterator:
- Sends email to person 1
- Sends email to person 2
- ... continues for all 5
## Accessing Current Item Data
Inside Iterator, use `{{iterator.currentItem}}` to access the current record:
| Variable | Description |
|----------|-------------|
| `{{iterator.currentItem}}` | The entire current record object |
| `{{iterator.currentItem.id}}` | Record ID |
| `{{iterator.currentItem.email}}` | Email field |
| `{{iterator.currentItem.company.name}}` | Related company name |
| `{{iterator.index}}` | Current position in array (0-based) |
In the variable picker, you can either drill into a specific field of the current item, or pick **Use the whole item** to reference the entire current item (`{{iterator.currentItem}}`). Selecting the whole item is handy when you want to pass a full record straight into a downstream step rather than rebuilding it field by field.
## Common Patterns
### Update Multiple Records
**Goal**: Mark all overdue tasks as "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
```
### Create Records from Array
**Goal**: Webhook receives order with multiple items, create a record for each
```
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}}
```
### Conditional Processing Inside Loop
**Goal**: Only send email to contacts with valid emails
```
1. Search Records (People)
2. Iterator (searchRecords)
└── Filter (currentItem.email is not empty)
└── Send Email
- To: {{iterator.currentItem.email}}
```
## Troubleshooting
### "Iterator expects an array"
**Cause**: You passed a single record instead of an array.
**Fix**: Make sure you're passing the result of Search Records or an array field, not a single record.
```
✅ Correct: {{searchRecords}}
❌ Wrong: {{searchRecords[0]}}
```
### Iterator Doesn't Run
**Cause**: The array is empty.
**Fix**: Add a Filter before Iterator to check array length:
```
Filter: {{searchRecords.length}} > 0
```
### Actions Run Too Many Times
**Cause**: Search Records returned more records than expected.
**Fix**:
- Add more specific filters to Search Records
- Set a limit on Search Records (max 200)
- Add Filter inside Iterator for additional conditions
## Performance Considerations
- **Credit usage**: Each iteration consumes credits for its actions
- **Time**: Large arrays take longer to process
- **Limits**: Consider batching very large operations
- **Rate limits**: External API calls may hit rate limits with many iterations
## Best Practices
1. **Always check array length** before Iterator to avoid errors
2. **Add filters inside loops** when not all items need processing
3. **Rename your Iterator step** to describe what it's looping through
4. **Test with small arrays** before processing large datasets
5. **Monitor workflow runs** to ensure iterations complete as expected
## Related
- [Workflow Actions](/user-guide/workflows/capabilities/workflow-actions)
- [How to Use Branches](/user-guide/workflows/capabilities/use-branches-in-workflows)
- [Workflows FAQ](/user-guide/workflows/how-tos/need-more-help/workflows-faq)

View File

@@ -0,0 +1,286 @@
---
title: Workflow Actions
description: Learn about the actions available in Twenty workflows.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## About Actions
Actions define what happens after a trigger fires. You can chain multiple actions together to build complex automations.
<Tip>
- Use the variable picker (click the `(x+)` icon) to browse available data from previous steps
- Hover over any input field to see which step a variable comes from — helpful when the same field (e.g., ID) exists in multiple previous steps
- Give each action a descriptive name for easier maintenance
</Tip>
## Record Actions
<img src="/images/user-guide/workflows/workflow_actions.png" style={{width:'100%'}}/>
### Create Record
Adds a new record to a selected object.
**Configuration**:
- Select the target object
- Fill out the required and optional fields
- Use data from previous steps or input values manually to populate fields
**Output**: The newly created record data is available for use in subsequent steps.
### Update Record
Modifies an existing record in a selected object.
<img src="/images/user-guide/workflows/update_record.png" style={{width:'100%'}}/>
**Configuration**:
- Select the target object
- Choose the specific record to update.
- You can either choose a fixed record, using the drop down menu displaying all available records.
- Or you can have the record dynamically selected, by designating a record found in a previous step, using the `(x+)`. You cannot search for the record based on different criteria at this stage. If you've not yet identified the record, add a `Search Record` step before this `Update Record` step.
- Select fields to modify and enter new values
**Output**: The updated record data is available for use in subsequent steps.
### Delete Record
Removes a record from a selected object.
**Configuration**:
- Select the target object
- Choose the specific record to delete
**Output**: The deleted record data remains available for use in subsequent steps.
### Search Records
Finds records within a selected object using filter conditions.
**Configuration**:
- Select the object to search
- Set filter criteria to narrow results
- Configure sorting and limits
**Output**: Returns matching records that can be used in subsequent steps.
<Note>
**Limit**: Search Records returns a maximum of **200 records**. If you need to process more, add specific filters to reduce results or use scheduled workflows to process in batches.
</Note>
**Best Practice**: Use [branches](/user-guide/workflows/capabilities/workflow-branches) after Search Records to handle "found" vs "not found" scenarios.
### Upsert Record
Creates a new record or updates an existing one based on matching criteria. This is useful when you're not sure if a record already exists.
<img src="/images/user-guide/workflows/upsert_double_identifier.png" style={{width:'100%'}}/>
**Configuration**:
- Select the target object
- Note which fields can be used for matching: email for People, domain for Companies, ID for any object, or any field marked as Unique. You'll need to populate at least one of these below.
- Fill out the field values. Do not forget to populate at least one of the unique identifiers.
<Note>
**Matching usually works even better when adding only one unique identifier.** For example, the screenshot below will match companies based on their domain. The ID is not necessarily needed.
</Note>
<img src="/images/user-guide/workflows/upsert_simple_identifier.png" style={{width:'100%'}}/>
- Use data from previous steps to populate fields
**How it works**:
1. Searches for a record matching your criteria
2. If found → updates the existing record
3. If not found → creates a new record
**Output**: The created or updated record data is available for use in subsequent steps.
## Flow Actions
### Iterator
**Loops through an array of records** returned from a previous step, allowing you to perform actions on each record individually.
**Configuration**:
- Select the array of records from a previous step (e.g., results from Search Records, from a Manual trigger with Bulk availability, from a Code node). When a Code or Logic Function step returns a top-level array, select its **Whole list** option to loop over the entire output.
- Define the actions to perform on each record in the loop.
<Note>
- You can add several actions within an Iterator.
- When using branches inside an Iterator, make sure the last step of each branch connects back to the Iterator to close the loop.
</Note>
- Access `Current Item` Fields: to use fields from the record currently being processed, click on the **Iterator** step, then select **Current item**. The list of available fields from that record will be displayed and can be selected for use in subsequent actions. You can also select **Use the whole item** to pass the entire current item into a downstream step.
<VimeoEmbed videoId="1146577247" title="Video demonstration" />
### Filter
Filters records based on specified conditions, allowing only records that meet the criteria to pass through.
**Configuration**:
- Select the record to filter
- Define filter conditions and criteria
- Configure which records should pass through to subsequent steps
<Note>
1. **Output**: Filter nodes don't return data—they act as gates. If the conditions are met, the workflow continues. If not, the workflow stops at that branch.
2. The `IS` operator can be used with numeric fields. It performs as an `EQUAL`.
</Note>
### Delay
Pauses workflow execution for a specified duration or until a specific date/time.
**Delay Types**:
| Type | Description |
|------|-------------|
| **Duration** | Wait for a specific amount of time (days, hours, minutes, seconds) |
| **Scheduled Date** | Wait until a specific date and time |
**Configuration for Duration**:
- Set days, hours, minutes, and/or seconds
- Combine multiple units (e.g., 2 days and 4 hours)
**Configuration for Scheduled Date**:
- Select a date and time
- Can reference a date field from a previous step (e.g., follow up 3 days after a meeting)
**Use cases**:
- Wait 24 hours before sending a follow-up email
- Pause until an opportunity's close date
- Schedule actions for business hours
<Note>
The scheduled date cannot be in the past. If a date field from a previous step is used and the date has already passed, the workflow will fail.
</Note>
**Limits & Credits**:
- **No maximum duration limit**—you can set delays of minutes, days, weeks, or longer
- **1 credit consumed** when the Delay node executes, regardless of duration
- **No credits consumed** while waiting—a 5-minute delay costs the same as a 5-day delay
## Communication Actions
### Send Email
Sends an email from your workflow. This is great for templated group emails. Emails will look like the ones you send from your mailbox.
It's not suited for newsletters (which require richer formatting) or automated email sequences.
**Prerequisites**: Add an email account in Settings → Accounts
**Configuration**:
- Select the sender email account
<Note>
You can only send emails from mailboxes synced to your own Twenty account. Sending from other team members' mailboxes (e.g., the account owner's email) is on the roadmap.
</Note>
For all the following steps, you can reference variables from previous steps for personalization.
- Enter recipient email address.
<Note>
Only one recipient is possible at the moment.
</Note>
- Set subject line.
- Compose message body. You can format links, create numbered lists, bullet point lists, and add attachments.
<Note>
Adding HTML signatures is not possible at the moment.
</Note>
### Form
Prompts a form during workflow execution to collect user input. The responses can then be used in subsequent steps to create records, send emails, or execute any other action based on the input.
<Warning>
**Forms are designed for manual triggers only**. For workflows with other triggers (Record Created, Updated, etc.), forms are only accessible via the workflow run interface, which is not the expected user experience. A notification center will be released in 2026 to properly support forms in automated workflows.
</Warning>
**Configuration**:
- Configure the fields that users will be asked to fill. For each field, choose
- a type among text, number, date, a given record, a select field. Fields from all objects are available for selection.
- a label
- a default value under `Placeholder` (optional)
- Edit the form title
**Output**: Form responses are available for use in subsequent steps.
**Example**: The "Quick Lead" workflow is available by default in all workspaces, available anywhere in the Command Menu `Cmd+K`.
**How to fill the form**:
- Trigger your manual workflow from the Command Menu `Cmd+K`
- Fill the form that is displayed in the side panel and click `Submit`.
<Note>
The fields cannot be made mandatory.
</Note>
<VimeoEmbed videoId="1147277989" title="Video demonstration" />
## Integration Actions
### Code
Runs custom JavaScript within your workflow.
Behind the scenes, each Code action is backed by its own **logic function** — a server-side TypeScript function that runs on the Twenty platform. When you add a Code action, Twenty creates a dedicated logic function for that step and exposes its editor inline, so the code you write lives with the workflow.
**Configuration**:
- Access variables from previous steps. You can edit the variables' names dynamically.
<VimeoEmbed videoId="1147281795" title="Video demonstration" />
- Write JavaScript code in the editor
- Return variables for use in subsequent steps
- Test code directly in the step
<Note>
If you need to use external API keys in your code, you must input them directly in the function body. You cannot configure API keys elsewhere and reference them in the logic function.
</Note>
<Tip>
**Working with arrays?** Arrays from external systems or previous steps may come as strings. See [How to handle arrays in Code actions](/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions) for the solution.
</Tip>
<Tip>
Click the square icon at the top right of the code editor to display it in full screen — helpful since the default editor width is limited.
</Tip>
#### Reusing a logic function across workflows
The inline Code action is great for one-off logic, but the code it holds belongs to that single step. When you want to share the same logic across several workflows — or maintain it as versioned source in an app — define a **reusable logic function** instead of copy-pasting code into each Code action.
A logic function is defined once in an app (using the SDK's `defineLogicFunction`) and exposed to the workflow builder by adding `workflowActionTriggerSettings`. Once your app is installed, that function appears as its own action in the workflow builder, alongside the built-in actions. Selecting it renders the input fields you declared (with variable pickers, just like other actions) and runs your shared code — no inline JavaScript required.
| | Code action | Reusable logic function |
|---|---|---|
| **Where the code lives** | Inline editor, tied to one workflow step | Defined in an app, versioned in source control |
| **Reuse** | Copy-pasted per step | Added as an action in any workflow |
| **Inputs** | Variables wired in the editor | Fields rendered from your declared input schema |
| **Best for** | Quick, workflow-specific logic | Shared logic used across multiple workflows |
Both run on the same logic function runtime, so a Code action you've prototyped inline can later be promoted into a reusable logic function with minimal changes.
<Tip>
For the developer-facing reference on defining logic functions and exposing them to the workflow builder, see [Logic Functions](/developers/extend/apps/logic/logic-functions) — in particular the **Exposing a function as an AI tool or workflow action** section and `workflowActionTriggerSettings`.
</Tip>
### HTTP Request
Sends a request to an external API as part of your workflow.
<img src="/images/user-guide/workflows/http_action.png" style={{width:'100%'}}/>
**Configuration**:
- Enter the API endpoint URL. Using parameters from previous steps is possible.
- Select HTTP method (GET, POST, PUT, PATCH, DELETE)
- Add required headers and values
- Provide sample response for structure preview
## AI Actions
### AI Agent - Coming Soon
Runs an AI agent within your workflow to perform intelligent tasks.
**Configuration**:
- **Agent**: Select an existing AI agent or use the default agent
- **Prompt**: Write the instruction for the AI agent
- Reference variables from previous steps in the prompt
**What AI Agents can do**:
- Analyze and summarize data
- Classify or categorize records
- Generate text content
- Make decisions based on data
- Interact with your CRM data using tools
**Output**: The AI agent's response is available for use in subsequent steps. If the agent has a structured output schema, the response will follow that format.
<Note>
AI Agent actions consume workflow credits based on the AI model used. See [Workflow Credits](/user-guide/workflows/capabilities/workflow-credits) for details.
</Note>
<Note>
AI agents respect role-based permissions. You can assign specific roles to agents under **Settings → Members → Roles** to control what data they can access. See [Permissions](/user-guide/permissions-access/capabilities/permissions) for details.
</Note>

View File

@@ -0,0 +1,69 @@
---
title: Workflow Branches
description: Create parallel paths and conditional logic in your workflows.
---
Branches allow you to split your workflow into multiple paths that can run simultaneously or conditionally based on your data.
<img src="/images/user-guide/workflows/branches.png" style={{width:'100%'}}/>
## How Branches Work
When you create multiple connections from a single node, each path becomes a branch. By default, **all branches execute in parallel**—they don't wait for each other.
## Creating Branches
### Add a New Branch
1. **Right-click on the main canvas** of the workflow (not on an existing node)
2. Click **Add node**
3. Choose the node type for your new branch
4. Drag an arrow from the bottom of the previous step to the top of this new action
5. Repeat to add more branches from the same node
<Note>
Each branch is independent. Adding a branch doesn't affect other existing paths from that node.
</Note>
### Visual Layout
Branches appear as parallel paths in the workflow editor. You can drag nodes to rearrange the visual layout without affecting execution.
## Conditional Branches
Since all branches run by default, use **Filter** nodes to control which paths actually execute:
| Branch | Filter Condition | Action |
|--------|------------------|--------|
| A | Stage = "Won" | Send congratulations email |
| B | Stage = "Lost" | Create follow-up task |
| C | Stage = "Negotiation" | Notify manager |
1. Create branches from your trigger or action
2. Add a **Filter** node as the first step of each branch
3. Configure each filter with mutually exclusive conditions
4. Add your actions after each filter
Only the branch(es) where the filter condition is met will continue executing.
## Merging Branches
**Branches don't automatically merge.** Each branch runs independently until it ends. You have full flexibility in how you handle this:
- **Option 1: Keep branches separate**
Each branch handles its own follow-up actions independently. This is the simplest approach when branches don't need to converge.
- **Option 2: Merge branches manually**
When creating your workflow, you can manually connect multiple branches to the same downstream action. Simply drag arrows from the end of each branch to a common node.
<Note>
While you can use a [Delay](/user-guide/workflows/capabilities/workflow-actions#delay) node to pause execution, it's not currently configurable to wait "until another branch ends."
</Note>
## Related
- [How to Use Branches in Workflows](/user-guide/workflows/capabilities/use-branches-in-workflows) - Step-by-step guide
- [Workflow Actions](/user-guide/workflows/capabilities/workflow-actions) - Available actions including Filter

View File

@@ -0,0 +1,73 @@
---
title: Workflow Credits
description: Understand workflow credit consumption and management.
---
Workflow credits power your automations in Twenty. Understanding how they work helps you optimize costs and manage your automation budget effectively.
## Credit Allocation
Workflow credits are allocated based on your billing cycle, not your plan tier:
| Billing Cycle | Credits |
|---------------|---------|
| **Monthly subscription** | 5 credits per month |
| **Yearly subscription** | 50 credits per year |
<Note>
5 monthly credits are generous for standard automations. Most teams won't exceed this limit with typical workflow usage. Additional credits are primarily needed for advanced Code actions and AI-powered workflows.
</Note>
## How Credit Consumption Works
Credits are consumed when workflows execute, not when you create them. Each workflow action consumes credits based on its complexity:
### Credit Consumption by Action Type
- **Basic internal operations**: Very low credit consumption
- Search Records
- Create Record
- Update Record
- Delete Record
- Form actions
- **Complex operations**: Higher credit consumption
- Code actions (JavaScript execution)
- HTTP Requests to external services
- **AI features**: Higher credit consumption
- AI Agent actions consume credits based on the AI model used
- More complex prompts and longer outputs use more credits
- **Delay actions**: Minimal credit consumption
- The Delay node consumes **1 credit** when it executes
- **No credits are consumed** during the wait period
- A 5-minute delay costs the same as a 5-day delay
### Real-Time Deduction
Credits are deducted in real-time as workflows execute. This means:
- Draft workflows don't consume credits
- Only active, running workflows use your credit allocation
- Failed workflows still consume credits for completed steps
## Managing Credits
### Check Credit Usage
1. Go to **Settings → Billing**
2. View your current credit consumption and remaining balance
3. Monitor usage patterns to optimize your workflows
### Purchasing Additional Credits
If you need more credits beyond your plan allocation:
1. Go to **Settings → Billing**
2. Click on the option to purchase additional credits. Packages of different sizes are available.
3. Credits are added to your current balance
## Best Practices
- **Batch Processing**: Use bulk operations and Iterator actions efficiently to reduce individual action calls
- **Manual Trigger Optimization**: For manual triggers, choose `Bulk` availability to process multiple records in a single workflow run
- Optimize Code actions for efficiency

View File

@@ -0,0 +1,84 @@
---
title: Workflow Runs
description: Monitor and manage workflow executions.
---
## About Runs
A **Run** is a record of a workflow execution. Every time a workflow is triggered—whether by a record event, schedule, manual action, or webhook—a new run is created.
## Viewing Runs
### From the Workflow Editor
1. Open the workflow you want to monitor
2. Click the **Runs** panel on the right side
3. See a list of recent runs with their status
### From the Workflow Runs View
1. Go to **Workflow Runs** in the sidebar
2. View runs across all workflows
3. Filter by status, workflow, or date
## Run Statuses
| Status | Description |
|--------|-------------|
| **Running** | Workflow is currently executing |
| **Completed** | Workflow finished successfully |
| **Failed** | Workflow encountered an error and stopped |
| **Waiting** | Workflow is paused (e.g., waiting for a Delay action or Form submission) |
## Run Details
Click on any run to see:
- **Status**: Current state of the run
- **Started at**: When the run began
- **Duration**: How long the run took
- **Trigger data**: The input that started the workflow
- **Step outputs**: Data returned by each step
- **Error messages**: If the run failed, what went wrong
## Step-by-Step Execution
Each run shows the progression through your workflow:
1. See which steps completed successfully
2. Identify where failures occurred
3. View the data passed between steps
4. Debug issues by examining step inputs and outputs
## Error Handling
When a run fails:
1. Open the failed run
2. Find the step that caused the failure
3. Check the error message for details
Common issues:
- Missing required fields
- Invalid data format
- External API errors
- Permission issues
## Re-running Workflows
If a run fails, you can:
- Fix the underlying issue and wait for the next trigger
- For manual workflows, trigger again with the same or updated data
- Review the workflow logic to prevent future failures
## Performance Tips
### Managing Run History
- Runs are retained for historical reference
- Very old runs may be archived automatically
- Export run data if you need to keep records
### Monitoring Best Practices
- Check runs regularly after activating new workflows
- Review failed runs to identify patterns
## Related
- [Workflow Triggers](/user-guide/workflows/capabilities/workflow-triggers)
- [Workflow Actions](/user-guide/workflows/capabilities/workflow-actions)
- [Workflow Troubleshooting](/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting)

View File

@@ -0,0 +1,125 @@
---
title: Workflow Triggers
description: Learn about the different triggers that start your workflows.
---
## About Triggers
Workflows always start with a single trigger that defines when the automation should run.
<img src="/images/user-guide/workflows/workflow_triggers.png" style={{width:'100%'}}/>
<Tip>
**Advanced objects are supported!** Beyond standard CRM objects (People, Companies, Opportunities), you can also trigger workflows and perform actions on:
- Workspace Members
- Calendar Events
- Messages (Emails)
- Tasks, Notes, and many other system objects
This opens up powerful automations like notifying team members when calendar events are created, or processing incoming emails automatically.
</Tip>
## Record is Created
Starts the workflow when a new record is created in a selected object (People, Companies, Opportunities, or any custom object).
**Configuration**: Select the object type to monitor for new records.
<Note>
- This trigger is great for records created by CSV, mailbox and calendar synchronization, or API.
- **It is not recommended for records created manually**: with this trigger, workflows start as soon as the record is created. Since Twenty UI offers auto-save on the fly (there is no edit mode and then a validation to save records), the workflow will be triggered before the user inputs all the fields.
To trigger this workflow on records created manually, it is recommended to use the trigger `Record is created or updated` instead.
</Note>
## Record is Updated
Starts the workflow when changes are made to an existing record.
**Configuration**:
- Select the object type
- Optionally specify which fields to monitor for changes
## Record is Updated or Created
Starts the workflow when a record is either created or updated in a selected object.
**Why This Matters**: This trigger is particularly helpful because records created via different methods behave differently:
- **API/CSV imports**: Records are created with all fields populated immediately
- **Manual creation**: Records are created first, then fields are added in subsequent updates
**Configuration**:
- Select the object type to monitor
- Optionally specify which fields to monitor for changes
- The workflow will trigger both on initial creation and any subsequent updates
## Record is Deleted
Starts the workflow when a record is removed from an object.
**Configuration**: Select the object type to monitor for deletions.
## Manual Trigger
Starts the workflow when triggered by a user action. This trigger can be accessed through the `Cmd+K` menu or via a custom button that will be displayed in the top navbar after selecting record(s).
<img src="/images/user-guide/workflows/add_to_newsletter_button.png" style={{width:'100%'}}/>
**Availability Configuration**:
Choose how the workflow should handle record selection:
- **Global**: No record is required to trigger this workflow. The workflow is triggered from the command menu `Cmd+K` anywhere (from any object) and does not use record(s) as input.
For **Single** and **Bulk** availability, the selected record(s) will be passed to your workflow. This is configured for a given object, and several records can be selected before triggering the workflow.
- **Single**: The workflow will run from beginning to end as many times as there are records selected.
<Note>
**Soft limit: 100 runs/minute**. Beyond this, workflows remain in "Not Started" status and are processed gradually—either by a background job or when another workflow enters the queue. This means you can select more than 100 records with a Single trigger; execution will just be slower.
</Note>
- **Bulk**: The workflow will run once, providing the entire list of records as input. This means the workflow needs to contain an [Iterator action](/user-guide/workflows/capabilities/workflow-actions#iterator).
<Note>
This is more advanced, and best for people who want to optimize the number of workflow runs.
</Note>
<img src="/images/user-guide/workflows/manual_trigger.png" style={{width:'100%'}}/>
**Additional Configuration**:
- Select the target object (for Single and Bulk availability)
- Choose a command icon for the workflow trigger
- Configure navbar placement (Pinned or Not Pinned)
**Access Methods**:
- `Cmd+K` menu to find and launch manual workflows
- Custom button in the top navbar (if configured)
## Time-Based Trigger: On a Schedule
Starts the workflow on a recurring basis you define.
**Configuration**:
- Select time unit (minutes, hours, days)
- Enter a value or use custom cron expressions for advanced scheduling
<Note>
**Timezone**: Scheduled workflows run in **UTC**. When setting hours for daily schedules, convert your local time to UTC.
</Note>
## External Trigger: Webhook
Starts the workflow when a GET or POST request is received from an external service.
<img src="/images/user-guide/workflows/webhook_trigger.png" style={{width:'100%'}}/>
**Configuration**:
- The workflow provides a unique webhook URL—copy this and add it to your external system as the endpoint to call.
- For POST requests, define the expected body structure so Twenty knows what data to expect. Add here the fields you will receive that will be needed below in your workflow.
- Configure authentication (coming soon).
## Choosing the Right Trigger
| Use Case | Recommended Trigger |
|----------|---------------------|
| New leads need processing | Record is Created |
| Data changes need sync | Record is Updated |
| Import/manual data handling | Record is Updated or Created |
| Cleanup after deletion | Record is Deleted |
| User-initiated action | Launch Manually |
| Recurring reports | On a Schedule |
| External integration | Webhook or On a Schedule|

View File

@@ -0,0 +1,76 @@
---
title: Workflow Versions
description: Manage workflow versions and drafts.
---
## About Versions
Every time you activate a workflow, a new version is created. This allows you to track changes over time and revert to previous configurations if needed.
## Version Statuses
| Status | Description |
|--------|-------------|
| **Draft** | Being edited, not yet published |
| **Active** | Live version responding to triggers |
| **Deactivated** | Previously active but manually stopped |
| **Archived** | Past versions kept for history |
## Working with Drafts
When you edit an active workflow, your changes are saved as a **draft**. The active version continues to run while you work on updates.
Once you're done editing, you can:
- **Activate**: Publish the draft as the new active version (the previous version is archived)
- **Discard**: Delete the draft and keep the current active version
## Version History
### Viewing Past Versions
1. Open the workflow
2. Click the **Versions** tab
3. See all previous versions with timestamps
### Restoring a Version
1. Find the version you want to restore
2. Click **Use as draft**
3. The version is copied to a new draft
4. Make any necessary updates
5. Activate when ready
## Best Practices
### Version Management
- Activate only when ready for production
- Keep meaningful changes between versions
- Document major changes in workflow names or descriptions
- Test in draft mode before activating
### Reverting Changes
- If a new version causes issues, restore the previous version
- Use the version history to track what changed
- Always test restored versions before activating
## Common Workflows
### Quick Edit
1. Make minor changes to an active workflow
2. Test in draft mode
3. Activate the new version
### Major Revision
1. Use the previous version as a starting point
2. Make significant changes in draft
3. Thoroughly test all scenarios
4. Activate when confident
### Rollback
1. Identify the issue with the current version
2. Find the last working version in history
3. Click **Use as draft**
4. Activate to restore the old behavior
## Related
- [Getting Started with Workflows](/user-guide/workflows/overview)
- [Workflow Runs](/user-guide/workflows/capabilities/workflow-runs)