English
Compose
Prepares a message — recipients, subject, body, attachments — without sending anything. Add “Send” after it to save a draft or send it; in between you can add an approval, a condition or a table write.
Compose prepares a message from fields you fill in: recipients, subject, body, attachments. It sends nothing and saves no draft. Link Send after it to save the message as a draft or send it; in between you can add an Approval, a condition or a table write.
Use Compose when the text is a template: an acknowledgement, a reminder, a forward with the attachments. Use Compose (AI) when the text must be written for each email.
At a glance
- Type:
mail.compose· version 1 - Category: Actions
- Kind: Step — one stage of a run
- Effect: No external effect (
none) — nothing is written outside Mankomail; safe to replay - Needs a carrier email: No
- Connection: None
- Inputs:
main - Outputs:
main
Parameters
to
To — Comma-separated addresses. Defaults to the sender of the triggering email — and to nothing when the trigger brings no email.
- Type: Text (
string) - Required: Yes
- Default: Depends on the trigger —
{{ email.from.email }}(email or manual trigger) ;""(empty) (called by another workflow) ;""(empty) (schedule, webhook or integration event) - 2000 characters at most
- Expressions:
{{ }}accepted
cc
Cc
- Type: Text (
string) - Required: No
- Default:
""(empty) - 2000 characters at most
- Expressions:
{{ }}accepted
bcc
Bcc
- Type: Text (
string) - Required: No
- Default:
""(empty) - 2000 characters at most
- Expressions:
{{ }}accepted
subject
Subject
- Type: Text (
string) - Required: Yes
- Default: Depends on the trigger —
Re: {{ email.subject }}(email or manual trigger) ;""(empty) (called by another workflow) ;""(empty) (schedule, webhook or integration event) - 500 characters at most
- Expressions:
{{ }}accepted
bodyText
Body — Draft text. Accepts {{ }} expressions: {{ email.from.name }}, {{ data.extract_1.number }}…
- Type: Long text (
text) - Required: Yes
- Default:
""(empty) - 50000 characters at most
- Expressions:
{{ }}accepted
replyInThread
Reply in thread — The reply is attached to the triggering email and stays in the conversation. Ticked by default under an email trigger, unticked under a schedule, a webhook or a call — there is then no thread, and publishing refuses it if it stays ticked.
- Type: Yes / no (
boolean) - Required: No
- Default: Depends on the trigger —
true(email or manual trigger) ;false(called by another workflow) ;false(schedule, webhook or integration event) - Needs a carrier email for:
true
attachments
Attachments — The attachments to carry: those of the triggering email — this is what makes a real "forward with attachments" — and the files added by an earlier step (a fetched deed, a signed PDF). Inline images (signature logos) are never carried over.
- Type: One choice (
options) - Required: Yes
- Default:
none - Options:
none— None: The draft carries its text only.fromTrigger— Those of the run: All of them: those of the incoming email, then the files added by earlier steps.fromTriggerFiltered— Those of the run, filtered: Only those whose type or name matches — the quote, not the signature.
attachmentMime
File type — Exact MIME type (application/pdf) or a whole family (image/*). Left empty, no attachment is kept.
- Type: Text (
string) - Required: No
- Default:
""(empty) - 200 characters at most
- Example:
application/pdf - Shown when:
attachmentsisfromTriggerFiltered - Expressions:
{{ }}accepted
attachmentNamePattern
File name — Simple pattern: * matches anything, ? one character (*.pdf, invoice-*). Case is ignored.
- Type: Text (
string) - Required: No
- Default:
""(empty) - 200 characters at most
- Example:
*.pdf - Shown when:
attachmentsisfromTriggerFiltered - Expressions:
{{ }}accepted
Outputs
main
Data produced
What this node adds to the run data, and how to read it in an expression. <step> stands for the step key: the node name turned into an identifier (see Data and expressions).
{{ data.<step>.message }}—object { to, cc?, bcc?, subject, bodyText?, inReplyToMessageId?, attachmentRefs? }. The message prepared, ready for a Send node. Point Send's Message to send field at this expression.to,ccandbccare arrays of addresses;cc,bccandbodyTextare absent when empty;inReplyToMessageIdis present only when the message replies in the thread;attachmentRefslists the chosen attachments by position.{{ data.<step>.message.subject }}—string. The rendered subject.{{ data.<step>.bodyPreview }}—string. The first 280 characters of the body, for display in the run detail.{{ data.<step>.bodyTruncated }}—boolean.truewhen the body is longer than the preview.{{ data.<step>.attachments }}—array of { position, filename, mime, size }. The attachments the message will carry. Empty when Attachments is None or nothing matched.{{ data.<step>.attachments.0.filename }}—string. The file name of the first chosen attachment.{{ data.<step>.summary }}—string. A one-line description of the message for the run detail (number of recipients, attachments), in the language of the member running the workflow.summaryKeyandsummaryParamshold the same sentence as a translation key and its parameters.
Example
Forward every supplier invoice to the accounting mailbox, with its PDF. Add a Compose node named Forward:
to: "accounting@example.com"
subject: "Invoice from {{ email.from.name }}: {{ email.subject }}"
bodyText: "Forwarded automatically. Original sender: {{ email.from.email }}."
replyInThread: false
attachments: fromTriggerFiltered
attachmentMime: "application/pdf"Then a Send node with message set to {{ data.forward.message }} and mode set to send. The step data of Forward reads:
json
{
"message": {
"to": ["accounting@example.com"],
"subject": "Invoice from Acme: Invoice F-2026-118",
"bodyText": "Forwarded automatically. Original sender: billing@acme.example.",
"attachmentRefs": [{ "position": 0 }]
},
"bodyPreview": "Forwarded automatically. Original sender: billing@acme.example.",
"bodyTruncated": false,
"attachments": [
{ "position": 0, "filename": "F-2026-118.pdf", "mime": "application/pdf", "size": 84211 }
],
"summary": "…"
}Defaults that depend on the trigger
Under an email trigger, Compose starts as a reply: To is {{ email.from.email }}, Subject is Re: {{ email.subject }} and Reply in thread is ticked. Under a schedule, a webhook, a polling trigger or a call, there is no sender and no thread: To and Subject start empty and must be filled in (from data.* for instance), and Reply in thread starts unticked. If Reply in thread stays ticked in a workflow without a triggering email, publishing refuses it, and at run time the node fails with mail.reply_without_email.
Attachments
- None: the message carries its text only.
- Those of the run: every attachment of the triggering email, then every file added by an earlier step (a document fetched from a drive, a signed PDF).
- Those of the run, filtered: only those whose type or name matches the filter fields. With both filter fields filled in, an attachment must match both; with both left empty, nothing is selected.
Inline images (signature logos, images embedded in the body) are never carried over. Attachments are referenced by position: their content is not copied into the step data, and the server reads the files when Send records the message.
Tips
- Addresses. To, Cc and Bcc take comma-separated addresses and are templatable. If To renders to no address, the node fails with
node_invalid_param. - No Message-ID. If the triggering email has no usable
Message-IDheader, the message is prepared outside the thread rather than lost. - No matching attachment. If the selection finds nothing, the message is still prepared, without attachments, and
attachmentsis empty. - Sender mailbox. Compose does not choose the sending mailbox or the signature: Send does.
- No effect. Compose behaves the same in test runs and in production; only Send is simulated.