English
Send
Saves a message as a draft or sends it, from the mailbox of your choice and with the signature of your choice. Goes after “Compose” — or is filled in by hand.
Send is the node that makes a message exist: saved as a draft in a mailbox, or sent for real. It is the only mail node whose effect is send, so it is the one held back in test runs, the one the organisation's node policy can restrict, and the one the send kill switch stops.
Send usually follows Compose or Compose (AI), which prepare the message without touching the mailbox. It can also be filled in by hand when there is nothing to write: a one-line acknowledgement, a templated reminder.
At a glance
- Type:
mail.send· version 1 - Category: Actions
- Kind: Step — one stage of a run
- Effect: Sends an email (
send) — sends an email; described instead of performed during a test run, stopped by the send kill switch - Needs a carrier email: No
- Connection: None
- Inputs:
main - Outputs:
main
Parameters
message
Message to send — The expression carrying a message composed upstream — {{ data.compose_1.message }}. Leave empty to compose the whole message below. Fields filled in here always win over the incoming message.
- Type: Text (
string) - Required: No
- Default:
""(empty) - 200000 characters at most
- Expressions:
{{ }}accepted
mode
What to do with it — A draft is reviewed before it leaves (it shows up in the morning Review and in the real mailbox). Sending goes out for real, under the organisation safeguards.
- Type: One choice (
options) - Required: Yes
- Default:
draft - Options:
draft— Save as draft: Nothing leaves. The message waits for a human to read it.send— Send: The message goes out for real, under the organisation’s kill switch and hourly cap. During a test run, nothing leaves.
mailbox
Sending mailbox — Which mailbox the message leaves from. Empty: the one of the triggering email. Required when the trigger is not an email (schedule, webhook, polling): there is then no mailbox to reuse, and publishing refuses it. A workflow triggered on one mailbox can write from another — that is what lets you answer from the service mailbox.
- Type: Remote resource (
resourceLocator) - Required: Depends on the trigger — yes: schedule, webhook or integration event
- Ways to choose: pick from a list, type an ID (
mail.mailbox)
to
To — Comma-separated addresses. Empty: those of the incoming message.
- Type: Text (
string) - Required: No
- Default:
""(empty) - 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 — Empty: the one of the incoming message.
- Type: Text (
string) - Required: No
- Default:
""(empty) - 500 characters at most
- Expressions:
{{ }}accepted
bodyText
Body — Empty: the body of the incoming message.
- Type: Long text (
text) - Required: No
- Default:
""(empty) - 200000 characters at most
- Expressions:
{{ }}accepted
threading
Thread
- Type: One choice (
options) - Required: Yes
- Default:
inherit - Options:
inherit— As the message says: The thread comes from the incoming message, which already knows whether it replies.reply— Reply in thread: The reply is attached to the triggering email and stays in its conversation. Refused at publication when the trigger is not an email: there is no thread.new— New thread: The message starts on its own, outside the original conversation.
- Needs a carrier email for:
reply
attachments
Attachments
- Type: One choice (
options) - Required: Yes
- Default:
fromMessage - Options:
fromMessage— Those of the message: The attachments the “Compose” node selected.none— None: The message leaves with none.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.
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
signature
Signature — A workflow does not sign on its own: with no explicit choice, the message leaves unsigned.
- Type: One choice (
options) - Required: Yes
- Default:
none - Options:
none— Nonemember— That of the running member: Their default signature, the one the composer uses.person— That of a chosen person: Write from the service mailbox and sign with the name of the person in charge.named— A specific signature
signatureMemberId
Person
- Type: Remote resource (
resourceLocator) - Required: No
- Ways to choose: pick from a list, type an ID (
org.member) - Shown when:
signatureisperson
signatureId
Signature
- Type: Remote resource (
resourceLocator) - Required: No
- Ways to choose: pick from a list, type an ID (
mail.signature) - Shown when:
signatureisnamed
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>.opId }}—string. The identifier of the recorded outbound operation. Stable when the step is retried. In a test run it issimulated:mail.draftorsimulated:mail.send.{{ data.<step>.simulated }}—boolean.truein a test run: nothing was saved or sent, the run detail describes what would have happened.{{ data.<step>.mode }}—string.draftorsend, as chosen in What to do with it.{{ data.<step>.mailboxId }}—string. The identifier of the mailbox actually used. In a test run, the mailbox chosen on the node, orsimulatedwhen none was chosen.{{ data.<step>.mailboxAddress }}—string. The address of the mailbox the message leaves from.{{ data.<step>.messageId }}—string. TheMessage-IDheader set on the message, stable when the step is retried. Useful to store in a table and find the message later. Absent in a test run.{{ data.<step>.sentAt }}—string or null. In send mode, the ISO 8601 instant the sending chain accepted the message (not the instant the provider delivered it).nullfor a draft and in a test run.{{ data.<step>.signature }}—string. The name of the signature added. Absent when the message is unsigned or in a test run.{{ data.<step>.message }}—object { to, cc, bcc, subject, inReplyToMessageId, attachments }. What was actually recorded, after combining the incoming message and the node fields:to,ccandbccas arrays (possibly empty),subject,inReplyToMessageId(nulloutside a thread) andattachmentsas an array of{ position, filename, mime, size }. The body is not repeated.{{ data.<step>.summary }}—string. A one-line description for the run detail (draft saved or message sent, from which mailbox, with which files), in the language of the member running the workflow.summaryKeyandsummaryParamshold the same sentence as a translation key and its parameters.
Example
A Compose (AI) node named Reply wrote an answer. Add a Send node:
message: "{{ data.reply.message }}"
mode: draft
mailbox: (empty)
threading: inherit
attachments: fromMessage
signature: memberThe draft is saved in the mailbox of the triggering email, in the thread (Compose (AI) replied in the thread), with the default signature of the member running the workflow. The step data reads:
json
{
"opId": "6f1c…",
"simulated": false,
"mode": "draft",
"mailboxId": "2b7e…",
"mailboxAddress": "sales@example.com",
"messageId": "<…@example.com>",
"sentAt": null,
"signature": "Default",
"message": {
"to": ["buyer@example.com"],
"cc": [],
"bcc": [],
"subject": "Re: Price for 200 units",
"inReplyToMessageId": "<CAF123@mail.example.com>",
"attachments": []
},
"summary": "…"
}Where the message comes from
Two sources combine, and the rule fits in one sentence: the incoming message gives the base, and any field filled in on Send wins over it.
- Message to send takes an expression pointing at a message prepared upstream, such as
{{ data.reply.message }}. The expression must point at themessageobject itself; anything else (plain text, another key) fails withnode_invalid_paramand a hint to point it at the output of a Compose node. - To, Cc, Bcc, Subject, Body replace the matching part of the incoming message when they are filled in. Left empty, the incoming value is kept. Leave Message to send empty to write the whole message on the node.
Because an empty field means "keep the incoming value", a copy cannot be removed from Send: prepare the message without it instead.
Draft or send
- Save as draft: nothing leaves. The draft appears in the real mailbox and in the Review, waiting for a human. Drafts are never held by the send kill switch or the hourly cap.
- Send: the message leaves for real, through the sending chain. If sending is suspended (the instance-wide
SEND_ENABLEDsetting or the organisation's own switch), the message is held, not lost, and leaves when sending reopens. The organisation's hourly cap (SEND_MAX_PER_HOUR, 100 per hour by default) delays messages beyond it rather than dropping them. See Review and approvals and Environment variables.
Sending mailbox
The mailbox is chosen in this order: the Sending mailbox field, then the sender proposed by the incoming message, then the mailbox of the triggering email. A workflow triggered on one mailbox can therefore answer from another, such as a shared service address. Only mailboxes of the member running the workflow can be used.
When the trigger brings no email (schedule, webhook, polling), the field is required and publishing refuses an empty value. If no mailbox can be found at run time, the step fails with mail.mailbox_required.
Thread
- As the message says (default): the thread comes from the incoming message. Compose and Compose (AI) already decided whether they reply.
- Reply in thread: forces
In-Reply-To/Referencesfrom the triggering email. It requires a triggering email: publishing refuses it otherwise, and at run time it fails withmail.reply_without_email. If the triggering email has no usableMessage-ID, the message leaves outside the thread rather than being lost. - New thread: the message starts on its own.
Attachments and signature
Attachments default to Those of the message, the files the Compose node selected. Those of the run and Those of the run, filtered pick from the triggering email's attachments and the files added by earlier steps; inline images are never included. The composed message, attachments included, must stay under the instance size limit (SEND_MAX_BYTES, 25 MB by default), otherwise the step fails with node_invalid_param.
A workflow does not sign on its own: with Signature set to None, the message leaves unsigned. That of the running member uses their default signature; That of a chosen person signs with someone else's default signature (write from the service mailbox, sign as the person in charge); A specific signature uses one picked from the list.
Tips
- Never sent twice. Send records an outbound operation keyed on the step. If the step is retried (worker crash, transient error), it finds the same operation and returns the same
opId: the message is not sent again. - Replaying a run resends. Replaying a whole run from the runs list starts a new run with new keys, so its Send steps send again. See Error handling.
- Test runs. In test runs nothing is written: no draft, no message, no outbound operation. The run detail says what would have been saved or sent. A test does not check the mailbox or the signature, except for a missing mailbox (
mail.mailbox_required), which fails in the test as it would in production. - Permanent refusals.
mail.mailbox_forbidden(the mailbox does not exist or is not the member's),mail.mailbox_cannot_send(disconnected, in error or without credentials: reconnect it) andmail.signature_not_foundare permanent and are not retried. Choosing That of a chosen person or A specific signature without picking one givesnode_invalid_param. - No recipient. If neither the incoming message nor To provides an address, the step fails with
node_invalid_param.