Skip to content

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: attachments is fromTriggerFiltered
  • 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: attachments is fromTriggerFiltered
  • 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, cc and bcc are arrays of addresses; cc, bcc and bodyText are absent when empty; inReplyToMessageId is present only when the message replies in the thread; attachmentRefs lists 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. true when 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. summaryKey and summaryParams hold 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-ID header, 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 attachments is 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.