Workflows in Twenty automate business processes by triggering actions in response to events. Build powerful automations without writing code, or use custom JavaScript for complex logic.
What are workflows?
A workflow consists of three components:
Trigger : The event that starts the workflow
Steps : The actions and logic to execute
Variables : Data passed between trigger and steps
Workflow triggers
Triggers define when a workflow should run.
Database event triggers
Run workflows when records are created, updated, deleted, or upserted.
Company Created
Person Updated
Opportunity Deleted
{
type : "DATABASE_EVENT" ,
eventName : "company.created" ,
// Access the new company via {{trigger.object}}
}
Event format : objectName.action
Actions: created, updated, deleted, upserted
Use lowercase object names: company, person, task
The triggered record is available in workflow steps via {{trigger.object.fieldName}}. For example: {{trigger.object.name}}, {{trigger.object.annualRevenue}}.
Manual triggers
Launch workflows on-demand from the UI.
Global
Single Record
Bulk Records
Available everywhere in the workspace. {
type : "MANUAL" ,
availability : {
type : "GLOBAL" ,
locations : [ "command-menu" , "sidebar" ]
}
}
Launch from a specific recordβs detail page. {
type : "MANUAL" ,
availability : {
type : "SINGLE_RECORD" ,
objectNameSingular : "company"
}
// Access via {{trigger.record.fieldName}}
}
Launch on multiple selected records. {
type : "MANUAL" ,
availability : {
type : "BULK_RECORDS" ,
objectNameSingular : "person"
}
// Access via {{trigger.records}}
}
Scheduled triggers (Cron)
Run workflows on a recurring schedule.
// Every day at 9 AM
"0 9 * * *"
// Every Monday at 8 AM
"0 8 * * 1"
// First day of month at midnight
"0 0 1 * *"
// Every hour
"0 * * * *"
// Every 15 minutes
"*/15 * * * *"
Webhook triggers
Start workflows from external systems via HTTP POST.
POST https://your-twenty-instance.com/webhooks/workflows/{workflow-id}
Headers:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Body:
{
"customData" : "value",
"anotherField" : 123
}
Webhook payload is available as {{trigger.payload}}.
Workflow actions
Actions are the steps that execute in your workflow.
Record operations
Create record
Create a new record in any object.
{
action : "CREATE_RECORD" ,
objectName : "task" ,
fields : {
title : "Follow up with {{trigger.object.name}}" ,
dueDate : "{{trigger.object.closeDate}}" ,
assignedTo : "{{trigger.object.ownerId}}" ,
status : "TODO"
}
}
Update record
Modify an existing record.
{
action : "UPDATE_RECORD" ,
objectName : "company" ,
recordId : "{{trigger.object.companyId}}" ,
fields : {
lastContactDate : "{{now}}" ,
status : "CONTACTED"
}
}
Find records
Query records with filters.
{
action : "FIND_RECORDS" ,
objectName : "opportunity" ,
filters : {
companyId : { eq : "{{trigger.object.id}}" },
stage : { ne : "CLOSED_WON" }
},
limit : 10
}
// Access results via {{step.output.records}}
Delete record
Soft-delete or permanently remove a record.
{
action : "DELETE_RECORD" ,
objectName : "task" ,
recordId : "{{step1.output.recordId}}" ,
permanent : false // true for hard delete
}
Email actions
Send email
Send emails through connected email providers.
{
action : "SEND_EMAIL" ,
to : [ "{{trigger.object.email}}" ],
cc : [ "[email protected] " ],
subject : "Welcome to {{workspaceName}}" ,
body : `
Hi {{trigger.object.firstName}},
Thank you for signing up. We're excited to work with you!
Best regards,
The Team
` ,
attachments : [ "{{step1.output.fileId}}" ]
}
Draft email
Create a draft email for manual review before sending.
{
action : "DRAFT_EMAIL" ,
to : [ "{{trigger.object.email}}" ],
subject : "Follow-up required" ,
body : "Draft content..." ,
relatedTo : {
objectName : "person" ,
recordId : "{{trigger.object.id}}"
}
}
HTTP request
Call external APIs.
{
action : "HTTP_REQUEST" ,
method : "GET" ,
url : "https://api.example.com/users/{{trigger.object.email}}" ,
headers : {
"Authorization" : "Bearer {{env.API_TOKEN}}" ,
"Content-Type" : "application/json"
}
}
// Access via {{step.output.body}}
{
action : "HTTP_REQUEST" ,
method : "POST" ,
url : "https://api.slack.com/api/chat.postMessage" ,
headers : {
"Authorization" : "Bearer {{env.SLACK_TOKEN}}"
},
body : {
channel : "#sales" ,
text : "New opportunity: {{trigger.object.name}} - ${{trigger.object.amount}}"
}
}
Conditional logic
If/Else branches
Execute different actions based on conditions.
{
action : "IF_ELSE" ,
condition : {
field : "{{trigger.object.amount}}" ,
operator : "greaterThan" ,
value : 50000
},
ifTrue : [
// Actions for large deals
{
action: "SEND_EMAIL" ,
to: [ "[email protected] " ],
subject: "Large opportunity alert"
}
],
ifFalse : [
// Actions for regular deals
{
action: "CREATE_RECORD" ,
objectName: "task" ,
fields: { title: "Follow up" }
}
]
}
Filter action
Stop workflow execution if conditions arenβt met.
{
action : "FILTER" ,
conditions : {
logicalOperator : "AND" ,
filters : [
{ field: "{{trigger.object.priority}}" , operator: "in" , value: [ "HIGH" , "URGENT" ] },
{ field: "{{trigger.object.status}}" , operator: "ne" , value: "COMPLETED" }
]
}
}
// Workflow stops here if conditions not met
Custom code
Write JavaScript for complex logic.
{
action : "CODE" ,
code : `
// Access workflow variables
const company = trigger.object;
const revenue = company.annualRevenue;
// Complex calculations
let category;
if (revenue > 10000000) {
category = "Enterprise";
} else if (revenue > 1000000) {
category = "Mid-Market";
} else {
category = "SMB";
}
// Return data for next steps
return {
category: category,
score: revenue / 1000000,
isHighValue: revenue > 5000000
};
`
}
// Access via {{step.output.category}}, {{step.output.score}}
Custom code runs in a sandboxed environment. You cannot import external packages or access the filesystem.
AI agent actions
Use AI to process data and make decisions.
{
action : "AI_AGENT" ,
agent : "default-agent" ,
prompt : `
Analyze this company and suggest next steps:
Company: {{trigger.object.name}}
Industry: {{trigger.object.industry}}
Revenue: ${ {trigger.object. annualRevenue } }
Provide:
1. Company profile summary
2. Recommended sales approach
3. Suggested follow-up timeline
` ,
outputSchema : {
summary : "string" ,
approach : "string" ,
followUpDays : "number"
}
}
Iterator (Loop)
Execute actions for each item in an array.
{
action : "ITERATOR" ,
items : "{{step1.output.records}}" ,
actions : [
{
action: "SEND_EMAIL" ,
to: [ "{{item.email}}" ],
subject: "Batch notification" ,
body: "Hi {{item.firstName}}..."
},
{
action: "UPDATE_RECORD" ,
objectName: "person" ,
recordId: "{{item.id}}" ,
fields: { lastNotified: "{{now}}" }
}
]
}
Wait before executing next steps.
{
action : "DELAY" ,
duration : 86400 , // seconds (24 hours)
unit : "SECONDS" // or "MINUTES", "HOURS", "DAYS"
}
Variables and expressions
Reference data throughout your workflow using variables.
Variable syntax
Use double curly braces: {{variableName}}
// Trigger data
{{ trigger . object . name }}
{{ trigger . object . email }}
{{ trigger . record . id }}
// Previous step outputs
{{ step1 . output . recordId }}
{{ findRecords . output . records [ 0 ]. name }}
// Environment variables
{{ env . API_KEY }}
{{ env . SLACK_WEBHOOK_URL }}
// System variables
{{ workspaceName }}
{{ currentUser . email }}
{{ now }} // Current timestamp
Accessing nested data
// Object fields
{{ trigger . object . address . city }}
{{ trigger . object . owner . email }}
// Array items
{{ step1 . output . records [ 0 ]. name }}
{{ trigger . object . emails [ 0 ]. email }}
// Composite fields
{{ trigger . object . fullName . firstName }}
{{ trigger . object . currency . amountMicros }}
Functions and operators
// String operations
{{ trigger . object . name . toUpperCase ()}}
{{ trigger . object . email . split ( '@' )[ 0 ]}}
// Math operations
{{ trigger . object . amount * 1.1 }} // Add 10%
{{ trigger . object . price / 100 }} // Convert cents to dollars
// Date operations
{{ trigger . object . createdAt | date ( 'YYYY-MM-DD' )}}
{{ now | addDays ( 7 )}} // 7 days from now
Test variables in the workflow builderβs test mode to verify data is accessible correctly.
Building a workflow
Navigate to Workflows
Go to Settings β Workflows or use the workflows section in the sidebar.
Create new workflow
Click βNew Workflowβ and provide:
Name : Descriptive workflow name
Description : What the workflow does
Status : Active or Inactive
Configure trigger
Choose trigger type and configure settings:
Database event: Select object and action
Manual: Set availability and locations
Cron: Enter schedule expression
Webhook: Copy webhook URL for external use
Add steps
Click β+β to add action steps:
Choose action type
Configure action settings
Use variables to reference previous data
Test each step individually
Test workflow
Use test mode to run workflow with sample data:
View execution logs
Check variable values at each step
Debug any errors
Activate workflow
Once tested, set status to βActiveβ to enable the workflow.
Example workflows
Lead nurturing sequence
Workflow: New Lead Follow-up
Trigger : person . created
Step 1 : Send welcome email
action : SEND_EMAIL
to : {{ trigger . object . email }}
subject : "Welcome to {{workspaceName}}"
Step 2 : Wait 3 days
action : DELAY
duration : 259200 // 3 days in seconds
Step 3 : Check if engaged
action : FIND_RECORDS
objectName : "activity"
filters : { personId : {{ trigger . object . id }} }
Step 4 : If no engagement , send follow - up
action : IF_ELSE
condition : {{ step3 . output . count }} == 0
ifTrue :
- SEND_EMAIL : "Are you still interested?"
ifFalse :
- CREATE_RECORD : Create task for sales rep
Deal stage automation
Workflow: Opportunity Stage Change
Trigger : opportunity . updated
Step 1 : Filter for stage changes
action : FILTER
condition : {{ trigger . object . stage }} == "CLOSED_WON"
Step 2 : Create onboarding tasks
action : CREATE_RECORD
objectName : "task"
fields :
title : "Send contract to {{trigger.object.companyName}}"
assignedTo : {{ trigger . object . ownerId }}
dueDate : {{ now | addDays ( 1 )}}
Step 3 : Notify team
action : HTTP_REQUEST
method : POST
url : {{ env . SLACK_WEBHOOK }}
body :
text : "π Deal closed: {{trigger.object.name}} - ${{trigger.object.amount}}"
Step 4 : Update company status
action : UPDATE_RECORD
objectName : "company"
recordId : {{ trigger . object . companyId }}
fields :
status : "CUSTOMER"
lastPurchaseDate : {{ now }}
Data enrichment
Workflow: Enrich New Company
Trigger : company . created
Step 1 : Check if website exists
action : FILTER
condition : {{ trigger . object . website }} is not empty
Step 2 : Call enrichment API
action : HTTP_REQUEST
method : GET
url : "https://api.clearbit.com/v2/companies/find?domain={{trigger.object.website}}"
headers :
Authorization : "Bearer {{env.CLEARBIT_API_KEY}}"
Step 3 : Update company with enriched data
action : UPDATE_RECORD
objectName : "company"
recordId : {{ trigger . object . id }}
fields :
industry : {{ step2 . output . body . category . industry }}
employeeCount : {{ step2 . output . body . metrics . employees }}
annualRevenue : {{ step2 . output . body . metrics . estimatedAnnualRevenue }}
description : {{ step2 . output . body . description }}
Best practices
Limit API calls : Cache results when possible
Use filters early : Stop execution if conditions arenβt met
Batch operations : Use iterators instead of multiple workflows
Avoid deep nesting : Keep workflows under 20 steps
Error handling
Add error notification steps to alert you when workflows fail.
Test workflows thoroughly before activating
Use try/catch patterns in custom code
Add fallback branches in if/else logic
Monitor workflow execution logs regularly
Security
Store API keys in environment variables, never hardcode
Use workspace-level secrets for sensitive credentials
Limit workflow permissions to necessary objects
Review workflow logs for suspicious activity
Maintenance
Document complex workflows with descriptions
Use descriptive step names
Version workflows before major changes
Deactivate unused workflows
Review and optimize slow-running workflows
Workflow limits
Be aware of rate limits and resource constraints:
Maximum 100 steps per workflow
API rate limits apply to HTTP requests
Email sending is subject to provider limits
Workflows timeout after 5 minutes
Next steps
Objects and fields Understand the data workflows operate on
Views and filters Create filtered views to identify records for workflows
API Reference Trigger workflows programmatically via API
Webhook Integration Connect external systems to trigger workflows