packages/twenty-docs/user-guide/workflows/how-tos/crm-automations/formula-fields.mdx
Twenty doesn't yet support native formula fields yet (coming in 2026), but you can achieve the same result using workflows. This workaround lets you automatically calculate and populate field values—from simple concatenations to complex business logic.
| Use Case | Formula Example |
|---|---|
| Full name | First Name + " " + Last Name |
| Expected amount | Amount × Probability |
| Days until due | Due Date - Today |
| Days in stage | Today - Stage Entry Date |
| Lead score | Points based on multiple criteria |
Goal: Automatically combine first and last name into a full name field.
Trigger: Record is Updated or Created (People)
Filter: Check that first name or last name changed
Code action:
export const main = async (params) => {
const { firstName, lastName } = params;
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ');
return { fullName };
};
{{code.fullName}}Goal: Multiply opportunity amount by probability to get expected amount.
See How to Show Expected Amount in Pipeline for the complete workflow.
Trigger: Record is Updated (Opportunities, Amount OR Probability field)
Code action:
export const main = async (params) => {
const { amount, probability } = params;
const expectedAmount = (amount || 0) * (probability || 0) / 100;
return { expectedAmount };
};
{{code.expectedAmount}}Goal: Calculate how many days remain until a task's due date.
Trigger: Record is Updated or Created (Tasks, Due Date field)
Code action:
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 };
};
{{code.daysUntilDue}}Goal: Score leads based on company size, industry, and engagement.
Trigger: Record is Updated (People or Companies)
Code action:
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 };
};
{{code.leadScore}}Goal: Automatically extract and store the email domain.
Trigger: Record is Updated (People, Email field)
Code action:
export const main = async (params) => {
const { email } = params;
if (!email) return { domain: null };
const domain = email.split('@')[1]?.toLowerCase();
return { domain };
};
{{code.domain}}