diff --git a/packages/twenty-docs/docs.json b/packages/twenty-docs/docs.json
index 60ba3cd4c7..2b997516d9 100644
--- a/packages/twenty-docs/docs.json
+++ b/packages/twenty-docs/docs.json
@@ -175,7 +175,8 @@
"user-guide/workflows/how-tos/crm-automations/formula-fields",
"user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -621,7 +622,8 @@
"l/fr/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/fr/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/fr/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/fr/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/fr/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/fr/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -1067,7 +1069,8 @@
"l/ar/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/ar/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/ar/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/ar/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/ar/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/ar/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -1513,7 +1516,8 @@
"l/cs/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/cs/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/cs/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/cs/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/cs/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/cs/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -1959,7 +1963,8 @@
"l/de/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/de/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/de/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/de/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/de/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/de/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -2405,7 +2410,8 @@
"l/es/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/es/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/es/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/es/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/es/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/es/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -2851,7 +2857,8 @@
"l/it/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/it/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/it/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/it/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/it/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/it/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -3297,7 +3304,8 @@
"l/ja/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/ja/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/ja/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/ja/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/ja/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/ja/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -3743,7 +3751,8 @@
"l/ko/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/ko/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/ko/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/ko/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/ko/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/ko/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -4189,7 +4198,8 @@
"l/pt/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/pt/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/pt/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/pt/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/pt/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/pt/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -4635,7 +4645,8 @@
"l/ro/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/ro/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/ro/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/ro/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/ro/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/ro/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -5081,7 +5092,8 @@
"l/ru/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/ru/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/ru/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/ru/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/ru/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/ru/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -5527,7 +5539,8 @@
"l/tr/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/tr/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/tr/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/tr/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/tr/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/tr/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
@@ -5973,7 +5986,8 @@
"l/zh/user-guide/workflows/how-tos/crm-automations/formula-fields",
"l/zh/user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"l/zh/user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "l/zh/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "l/zh/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "l/zh/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/ai-triage.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/ai-triage.png
new file mode 100644
index 0000000000..ed082df541
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/ai-triage.png differ
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/find-sender.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/find-sender.png
new file mode 100644
index 0000000000..c7bcb41aa9
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/find-sender.png differ
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/gmail-threaded-reply.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/gmail-threaded-reply.png
new file mode 100644
index 0000000000..5b19434110
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/gmail-threaded-reply.png differ
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/search-records.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/search-records.png
new file mode 100644
index 0000000000..4f122261c5
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/search-records.png differ
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/send-email.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/send-email.png
new file mode 100644
index 0000000000..1899648990
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/send-email.png differ
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/should-reply.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/should-reply.png
new file mode 100644
index 0000000000..daec20939e
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/should-reply.png differ
diff --git a/packages/twenty-docs/images/user-guide/workflows/auto-reply/workflow-overview.png b/packages/twenty-docs/images/user-guide/workflows/auto-reply/workflow-overview.png
new file mode 100644
index 0000000000..1a0a04b5de
Binary files /dev/null and b/packages/twenty-docs/images/user-guide/workflows/auto-reply/workflow-overview.png differ
diff --git a/packages/twenty-docs/navigation/base-structure.json b/packages/twenty-docs/navigation/base-structure.json
index 3707010e57..fe476658cb 100644
--- a/packages/twenty-docs/navigation/base-structure.json
+++ b/packages/twenty-docs/navigation/base-structure.json
@@ -157,7 +157,8 @@
"user-guide/workflows/how-tos/crm-automations/formula-fields",
"user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
diff --git a/packages/twenty-docs/navigation/navigation-schema.json b/packages/twenty-docs/navigation/navigation-schema.json
index bb95be1ab1..2ad19a5db1 100644
--- a/packages/twenty-docs/navigation/navigation-schema.json
+++ b/packages/twenty-docs/navigation/navigation-schema.json
@@ -156,7 +156,8 @@
"user-guide/workflows/how-tos/crm-automations/formula-fields",
"user-guide/workflows/how-tos/crm-automations/display-related-record-data",
"user-guide/workflows/how-tos/crm-automations/closed-won-automations",
- "user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities"
+ "user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities",
+ "user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails"
]
},
{
diff --git a/packages/twenty-docs/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails.mdx b/packages/twenty-docs/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails.mdx
new file mode 100644
index 0000000000..8aa7105302
--- /dev/null
+++ b/packages/twenty-docs/user-guide/workflows/how-tos/crm-automations/auto-reply-to-inbound-emails.mdx
@@ -0,0 +1,121 @@
+---
+title: Auto-Reply to Inbound Emails
+description: Build a workflow that uses AI to triage incoming emails and send threaded replies automatically.
+---
+
+Respond to inbound emails in seconds — not hours. This workflow uses an AI Agent to filter out noise (newsletters, spam, auto-replies) and draft a personalized response to real messages, then sends it as a threaded reply inside the original conversation.
+
+## How email threading works
+
+Every email carries a hidden `Message-ID` header — a unique fingerprint assigned by the sender's mail server. When you reply to an email, your mail client sets an `In-Reply-To` header referencing that fingerprint. That's how Gmail, Outlook, and every other client groups messages into threads.
+
+In Twenty, that fingerprint is stored as `headerMessageId` on the Message object. Your workflow grabs it and passes it into the Send Email action's In-Reply-To field.
+
+## Building the workflow
+
+### Step 1: Create a new workflow
+
+Head to **Settings -> Workflows** and click **+ New Workflow**.
+
+### Step 2: Trigger on incoming messages
+
+Pick **When a Record is Created** and choose **Messages**.
+
+Every time an email lands in Twenty, this fires.
+
+
+
+### Step 3: Look up who sent it
+
+Add a **Search Records** action.
+
+The sender's address isn't on the message itself — it's on the related Message Participant record.
+
+| Field | Value |
+|-------|-------|
+| **Object** | Message Participants |
+| **Filter** | Message **is** `{{trigger.id}}` |
+| **Filter** | Role **is** From |
+| **Limit** | 1 |
+
+This gives you the sender's email in `handle` and their name in `displayName`.
+
+
+
+### Step 4: AI triage and draft reply
+
+Add an **AI Agent** action. This single step does two things: decides whether the email deserves a reply, and if so, writes one.
+
+Use a prompt like:
+
+```
+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.
+```
+
+The AI Agent outputs its response in a `response` field that the next steps can reference.
+
+
+
+### Step 5: Branch on the AI decision
+
+Add an **If/Else** action to check whether the AI decided to reply or skip.
+
+| Field | Value |
+|-------|-------|
+| **Condition** | AI Agent `response` **does not contain** `SKIP` |
+| **If true** | Continue to Send Email |
+| **Else** | Do nothing (workflow ends) |
+
+Spam, newsletters, and auto-generated messages get dropped. Everything else moves to the next step.
+
+
+
+### Step 6: Send a threaded reply
+
+Add a **Send Email** action on the "if true" branch. Click **Advanced options**, then **Add In-Reply-To**.
+
+| Field | Value |
+|-------|-------|
+| **To** | `{{Find Sender.first.handle}}` |
+| **Subject** | `Re: {{trigger.subject}}` |
+| **Body** | `{{AI Triage & Draft Reply.response}}` |
+| **In-Reply-To** | `{{trigger.headerMessageId}}` |
+
+The In-Reply-To field is what makes this a reply instead of a new conversation. The recipient sees it threaded under the original email in Gmail, Outlook, or any other client.
+
+
+
+
+**In-Reply-To** expects a `message.headerMessageId` from the trigger — it's the email's unique fingerprint, not a recipient address. If you leave it empty, the email still sends, just as a standalone message.
+
+
+
+Gmail uses the subject line to group messages into threads. The subject **must** start with `Re:` (including the colon and space) for Gmail to display the reply inside the original thread. Without it, the reply will appear as a separate conversation — even if the In-Reply-To header is set correctly.
+
+
+### Step 7: Test and activate
+
+Hit **Test**, then check your email client. The reply should appear nested under the original message.
+
+Activate when you're happy with it.
+
+## Ideas to build on
+
+- **Only reply to VIPs** — add a branch that checks the sender's domain or whether they exist as a Contact in Twenty
+- **Route by intent** — use separate AI Agent prompts to handle sales inquiries differently from support requests
+- **Enrich before replying** — add a Search Records step to pull the sender's company or deal history into the AI prompt for more personalized replies
diff --git a/packages/twenty-front/src/modules/workflow/types/EmailFormData.ts b/packages/twenty-front/src/modules/workflow/types/EmailFormData.ts
index bcdaecd58f..7d151c1bca 100644
--- a/packages/twenty-front/src/modules/workflow/types/EmailFormData.ts
+++ b/packages/twenty-front/src/modules/workflow/types/EmailFormData.ts
@@ -9,4 +9,5 @@ export type EmailFormData = {
subject: string;
body: string;
files: WorkflowAttachment[];
+ inReplyTo: string;
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionEmailBase.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionEmailBase.tsx
index 56a4bde4cc..88bce32133 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionEmailBase.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionEmailBase.tsx
@@ -75,12 +75,14 @@ export const WorkflowEditActionEmailBase = ({
const [visibleAdvancedFields, setVisibleAdvancedFields] = useState<{
cc: boolean;
bcc: boolean;
+ inReplyTo: boolean;
}>(() => {
const inputRecipients = action.settings.input.recipients;
return {
cc: Boolean(inputRecipients?.cc),
bcc: Boolean(inputRecipients?.bcc),
+ inReplyTo: Boolean(action.settings.input.inReplyTo),
};
});
@@ -89,7 +91,9 @@ export const WorkflowEditActionEmailBase = ({
const advancedOptionsDropdownId = `${action.id}-email-advanced-options`;
const hasAvailableAdvancedOptions =
- !visibleAdvancedFields.cc || !visibleAdvancedFields.bcc;
+ !visibleAdvancedFields.cc ||
+ !visibleAdvancedFields.bcc ||
+ !visibleAdvancedFields.inReplyTo;
const handleReauthorize = async () => {
if (!isDefined(missingScopes)) {
@@ -276,6 +280,18 @@ export const WorkflowEditActionEmailBase = ({
VariablePicker={WorkflowVariablePicker}
/>
)}
+ {visibleAdvancedFields.inReplyTo && (
+ {
+ handleFieldChange('inReplyTo', value);
+ }}
+ VariablePicker={WorkflowVariablePicker}
+ />
+ )}
{!actionOptions.readonly && hasAvailableAdvancedOptions && (
)}
+ {!visibleAdvancedFields.inReplyTo && (
+