Skip to content
Expedify
Actions — making things happen

Module · Replying and messaging

Email nodes

Lesson 7 of 21 · 8 min

Chat replies reach whoever is on the page. Email reaches whoever is not — the contact who filled in a form an hour ago, the list you want to tell about a release, the person whose deal just moved and does not know yet.

Expedify splits that into two nodes with almost the same shape. Email Send puts one message in front of one person. Email Bulk Send takes the same content and fans it out to a list, in batches, and tells you how many landed. Learn the first and you have most of the second.

The thing worth knowing before either of them: the body is not a text box that happens to accept a few tags. It is an HTML document. Paste a designed template — <head>, embedded CSS, media queries, the lot — and it sends as designed.

Email Send

Six of twelve. The rest are sender overrides and provider template fields.

integration_id

What it holds
Email integration ID (optional - auto-selects if not provided)

to_email

What it holds
Recipient email address. Multiple addresses may be separated by comma, semicolon, or newline.

subject

What it holds

html_content

What it holds

text_content

What it holds

from_email

What it holds

Leave integration_id empty and the org default is used. Set it when you have more than one sending domain and this message must come from a particular one. Everything else on that list is templatable, including to_email — which is how one node serves every contact rather than one.

Always fill text_content too. It is the version that shows when a client will not render HTML, and it is one of the signals that separates a wanted email from a filtered one. It costs a sentence.

A worked example

A contact is created in the CRM. One email goes out, addressed and written from the record that caused the run.

Trigger to inbox, two nodes

The body is a full HTML document, not a paragraph.

Open the body in that node and read it as what it is: a document with a <style> block, a card, and a button that is really a styled anchor. That is the whole trick to email design — there is no component library, there is CSS you inline and hope for. Whatever your designer exports, paste it here and template the values.

Note where the values come from. {{universaldatabasetrigger_1.new_data.first_name}} is the row that fired the trigger, and it appears in the subject and twice in the body. A database trigger hands you new_data and old_data; everything the email says about the person comes out of the first one.

Email Bulk Send

The four fields that differ from Email Send. Subject, body and sender behave identically.

recipients_source

What it holds
Defaults to config.

recipients_input_variable

What it holds

recipients

What it holds

batch_size

What it holds
Defaults to 100.

The only real decision is where the list comes from. recipients_source takes four values, and the panel labels them Manual List, From Input, Contact List and Segment:

  • config — Manual List. Addresses you typed into recipients. Fine for an internal announcement, useless for anything that changes.
  • input — From Input. A list an upstream node produced, named in recipients_input_variable. This is the one you will use.
  • contact_list — a saved contact list, by id.
  • segment — a segment, evaluated when the workflow runs. The list is whoever qualifies at send time, not whoever qualified when you built it.

Search, then send to whatever it found

recipients_source is input, and the variable points at the search's results array.

Scroll for all 3 steps →

Batch size is a courtesy to your provider, not a limit on the send. It defaults to 100 and caps at 1000, and it controls how many addresses go in one call — not how many people get the email. Lower it if your provider rate-limits you; otherwise leave it.

Two fields worth knowing that the config table above cannot show you, because the node declares them in a second schema the tooling does not read: track_opens and track_clicks. Both default off. The first adds a tracking pixel, the second rewrites your links through a proxy. They work — but see the warning below before you rely on reading them from a schema.

Which one, and which of its neighbours

When
One person, one message, triggered by something that happened to them. Welcome mails, receipts, notifications, a nudge when a deal stalls.

When
A list, one piece of content, written here. Announcements, weekly digests, anything where the body is the same for everyone.

When
A list, and the content lives in a saved template rather than in the node. Reporting, unsubscribes and campaign analytics come with it.

The line between the last two is where the content lives. Bulk Send carries its HTML in the node, so the workflow is the source of truth and nobody without builder access can change the words. Campaign Send points at a template someone edits elsewhere. Marketing usually wants the second; operations usually wants the first.

What breaks

Bulk Send declares its fields twice, and the two lists disagree. The class-level schema lists twelve fields. A second schema on the same node adds track_opens, track_clicks, personalization and three more. The node's code reads the tracking pair at run time, so they function — but anything that discovers the node's configuration from the declared schema will not know they exist, which includes agents building workflows for you. If you want tracking, set it in the panel and check the saved workflow.

A search that returns nothing sends nothing, and reports success. Bulk Send with recipients_source set to input is only as good as the node above it. An empty array is not an error — the node completes, {{alias.total_recipients}} is zero, and the run is green. Put a Condition on {{alias.total}} from the search before the send if the workflow is supposed to alert someone when the list is empty.

Bulk Send is the fastest way to mail the wrong people. The search in the example has no filter and a limit of 500, which is fine for a digest everyone opted into and wrong for anything else. Build the recipient list first, run only the search, read the count, and add the send afterwards. Nothing in the builder makes you do this and everyone who has skipped it once now does it every time.

Try it

  1. Build the single send: a database trigger on contacts insert, then an Email Send with to_email set to {{alias.new_data.email}}. Create a contact with your own address and watch it arrive.
  2. Replace the body with a designed HTML file — any marketing template you have. Paste the whole document, <head> and all, and send it to yourself. This is the step that changes what you think this node is.
  3. Now add text_content and send again. Compare how the two versions look in your client's preview pane.
  4. Swap in Bulk Send behind a CRM search, but set the search limit to 1 first. Read {{alias.total_recipients}} and {{alias.emails_sent}} before you raise it.

Next: WhatsApp Output — the same job on a channel where you do not get to choose when you are allowed to speak.