MailLandDocs

Automations

Set up trigger-based email sequences that run automatically based on subscriber behavior

Automations let you create multi-step email sequences triggered by subscriber events. Define a trigger, build a sequence of steps, and MailLand executes the flow automatically whenever the trigger fires.

Automation Triggers

Each automation starts with a single trigger:

TriggerValueDescription
SubscriberJoins0Subscriber is added to a mailing list
SubscriberUpdated1Subscriber profile is updated
CampaignOpened2Subscriber opens a specific campaign
CampaignClicked3Subscriber clicks a link in a campaign
DateBased4Birthday-only trigger: evaluates Subscriber.Birthday (see callout below)
Manual5Triggered manually via API

DateBased only ever evaluates Subscriber.Birthday, not an arbitrary recurring date field. Birthday is settable through POST/PUT /v1/marketing/subscribers (see Subscribers) and through CSV import, which accepts an optional birthday column, so a DateBased automation enrols any subscriber whose birthday was set either way.

Automation Statuses

StatusValueDescription
Draft0Editable, not running
Active1Running: new subscribers are enrolled on trigger
Paused2Paused: no new enrollments, and further steps stop for subscribers already enrolled. Reactivating (Active) resumes them from exactly where they held, and delivers everything that fell due while paused
Archived3Archived: hidden from default views. Like Paused, this stops new enrolments; further steps also stop for subscribers already enrolled. There is no unarchive endpoint, so this hold is permanent for those enrolments

Step Types

Each automation contains an ordered sequence of steps:

Step TypeValueDescription
SendEmail0Send an email to the subscriber
Wait1Wait for a duration before continuing
Condition2Evaluates a subscriber field; a gate that continues on true and ends the enrolment on false (see Condition step below)
AddToList3Add subscriber to a mailing list
RemoveFromList4Remove subscriber from a mailing list
UpdateSubscriber5Update subscriber fields
Webhook6Send a webhook to an external URL

Personalization

Automation email steps support {{variable}} merge tags, substituted per recipient from the enrolled subscriber's own record.

TagValue
{{firstName}}Subscriber's first name
{{lastName}}Subscriber's last name
{{fullName}}First and last name joined
{{email}}Subscriber's email address
{{subscriberId}}Opaque subscriber identifier
{{custom_<field>}}Any custom field on the subscriber, also available as {{<field>}}
Personalized automation step
<p>Hi {{firstName}}, here's what's new this month.</p>

A tag with no value is left exactly as written: {{firstName}} for a subscriber with no first name stays {{firstName}} in the delivered email rather than becoming an empty gap. Write content that reads correctly either way, e.g. Hi there{{firstName}} is a poor fit but "Your account" is safe. Use a fallback greeting when you are not sure every subscriber has the field.

Values are escaped for the part they land in: HTML content is HTML-escaped (so a name containing & or < cannot break your markup), while the subject line and plain-text body are never escaped, Ben & Jerry's stays exactly that.

The older snake_case spellings ({{first_name}}, {{last_name}}, {{full_name}}, {{subscriber_id}}) continue to work as aliases.

Create an Automation

POST https://api.mail.land/v1/marketing/automations
Create automation request
{
  "name": "Welcome Series",
  "description": "3-email welcome sequence for new subscribers",
  "trigger": 0,
  "mailingListId": "7h3n5k8w2q9m1t",
  "campaignId": null,
  "steps": [
    {
      "type": 0,
      "order": 1,
      "templateId": "2p9x4k7m3q8n1z",
      "subject": "Welcome to our newsletter!"
    },
    {
      "type": 1,
      "order": 2,
      "delayMinutes": 1440
    },
    {
      "type": 0,
      "order": 3,
      "templateId": "9k4p2x7m1q3n8z"
    }
  ]
}
FieldTypeRequiredDescription
namestringYesAutomation name (unique per workspace)
descriptionstringNoAutomation description
triggerintYesTrigger type (see triggers table)
mailingListIdstringNoTarget mailing list for trigger
campaignIdstringNoOptional for CampaignOpened / CampaignClicked triggers: when omitted, the trigger fires on any campaign, not just one
stepsarrayYesOrdered sequence of steps

There is no segmentId field on an automation: that field does not exist here (segments are used elsewhere, e.g. campaigns). SendEmail step content comes from templateId, not inline htmlContent/textContent/fromName/fromEmail.

campaignId is optional for the open/click triggers, not required as earlier versions of this page stated. It also fires on machine opens (Apple Mail Privacy Protection prefetch, security link scanners) that MailLand does not filter out today.

Step Fields

Every step is actually shaped { order, type, delayMinutes, templateId, subject, settings: {} }: there is no automation-level segmentId, and steps do not have the per-type fields shown in older versions of this page (conditionField, targetMailingListId, updateField, webhookUrl, fromName, fromEmail, replyTo are not top-level step fields). The tables below reflect the real shape.

SendEmail step (type: 0):

FieldTypeRequiredDescription
typeintYes0
orderintYesStep position in sequence
delayMinutesintNoMinutes to wait before this step runs: including when it is the first step: the delay before the very first send is now honoured at enrolment (it used to be ignored)
templateIdstringYesTemplate supplying the email content: content comes only from the template
subjectstringNoOverrides the template's subject if set

There is no inline htmlContent/textContent/fromName/fromEmail/replyTo on a step. The sender is fixed to noreply@<first fully verified domain>, shown with the workspace name: it is not configurable per step.

templateId is required on every SendEmail step. Create/update requests without one are refused with 400 VALIDATION_FAILED; a templateId that does not resolve to a template in your tenant is refused with 400 TEMPLATE_NOT_FOUND, both before anything is written. If a template is deleted after the automation is saved, the next subscriber to reach that step has their enrolment marked Failed instead of the step being silently skipped, and it counts toward the automation's totalFailed (see Get Automation Details).

Wait step (type: 1):

FieldTypeRequiredDescription
typeintYes1
orderintYesStep position
delayMinutesintYesMinutes to wait before next step

Condition step (type: 2):

A Condition step is a gate, not a branch: there is no second path in a linear step list. When it evaluates true, the enrolment continues to the next step. When it evaluates false, the enrolment ends there (Completed) and no later step runs for that subscriber.

FieldTypeRequiredDescription
typeintYes2
orderintYesStep position
settings.conditionFieldstringYesThe subscriber field to evaluate: email, firstName, lastName, phone, company, source, or custom_<name> for a custom field
settings.conditionOperatorstringYesequals, notEquals, contains, notContains, startsWith, endsWith, isEmpty, isNotEmpty (case-insensitive)
settings.conditionValuestringRequired unless the operator is isEmpty or isNotEmptyThe value to compare against

Comparisons are case-insensitive string comparisons, the same operators segments already offer. 400 VALIDATION_FAILED is returned at create/update time when conditionField is unknown, conditionOperator is not one of the values above, or conditionValue is missing for an operator that needs one.

An automation whose Condition step was saved before this validation existed (so its settings do not evaluate) keeps passing through unchanged, for continuity: it is only new create/update requests that are validated.

AddToList / RemoveFromList steps (type: 3, 4):

FieldTypeRequiredDescription
typeintYes3 or 4
orderintYesStep position
settings.mailingListIdstringYesMailing list to add/remove the subscriber, read from settings

UpdateSubscriber step (type: 5):

FieldTypeRequiredDescription
typeintYes5
orderintYesStep position
settingsobjectYesEach key in settings is written as a custom field on the subscriber

Webhook step (type: 6):

FieldTypeRequiredDescription
typeintYes6
orderintYesStep position
settings.urlstringYesURL to send the POST request to, read from settings

Webhook step requests are signed with a secret of the step's receiver (X-MLND-Signature, verified like a customer webhook): each distinct step URL has its own secret, so no receiver can sign for another. Read them with GET /v1/marketing/automations/{id}/webhook-signing-secrets and rotate them with POST /v1/marketing/automations/{id}/webhook-signing-secrets/regenerate, see Automation Webhook steps. Send { "webhookUrl": "…" } to rotate one receiver only.

Create an automation
curl -X POST https://api.mail.land/v1/marketing/automations \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome Series",
    "trigger": 0,
    "mailingListId": "7h3n5k8w2q9m1t",
    "steps": [
      {
        "type": 0, "order": 1,
        "subject": "Welcome!",
        "templateId": "2p9x4k7m3q8n1z"
      },
      { "type": 1, "order": 2, "delayMinutes": 1440 },
      {
        "type": 0, "order": 3,
        "subject": "Getting started",
        "templateId": "9k4p2x7m1q3n8z"
      }
    ]
  }'

Response

Automation response
{
  "id": "4f8k2m9p1q6z3s",
  "name": "Welcome Series",
  "description": "3-email welcome sequence for new subscribers",
  "trigger": "subscriberJoins",
  "triggerName": "SubscriberJoins",
  "status": "draft",
  "statusName": "Draft",
  "mailingListId": "7h3n5k8w2q9m1t",
  "mailingListName": "Product Updates",
  "campaignId": null,
  "campaignName": null,
  "stepsCount": 3,
  "totalEnrolled": 0,
  "totalCompleted": 0,
  "activatedAt": null,
  "createdAt": "2026-02-17T12:00:00Z",
  "updatedAt": "2026-02-17T12:00:00Z"
}

There is no segmentId/segmentName field on an automation, and the enrollment counters are totalEnrolled/totalCompleted (not totalEnrollments/activeEnrollments/completedEnrollments). The detail response also adds steps (the full step list), completionRate, and totalFailed: enrolments that reached a SendEmail step with no template in the tenant (missing or since deleted).

List Automations

GET https://api.mail.land/v1/marketing/automations
ParameterTypeDefaultDescription
cursorstring-Opaque cursor from the previous page's nextCursor. Omit for the first page.
limitint20Items per page
searchstring-Search by name
statusint-Filter by automation status
triggerint-Filter by trigger type
mailingListIdstring-Filter by mailing list
sortBystring-Sort field
sortDescboolfalseSort descending
List automations
curl https://api.mail.land/v1/marketing/automations \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Get Automation Details

GET https://api.mail.land/v1/marketing/automations/{id}

Returns the full automation including all steps:

Get automation details
curl https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Automation detail response
{
  "id": "4f8k2m9p1q6z3s",
  "name": "Welcome Series",
  "description": "3-email welcome sequence for new subscribers",
  "trigger": "subscriberJoins",
  "triggerName": "SubscriberJoins",
  "status": "active",
  "statusName": "Active",
  "mailingListId": "7h3n5k8w2q9m1t",
  "mailingListName": "Product Updates",
  "campaignId": null,
  "campaignName": null,
  "stepsCount": 3,
  "totalEnrolled": 230,
  "totalCompleted": 200,
  "totalFailed": 4,
  "activatedAt": "2026-01-10T08:05:00Z",
  "steps": [
    {
      "order": 1,
      "type": "sendEmail",
      "typeName": "SendEmail",
      "delayMinutes": null,
      "templateId": "2p9x4k7m3q8n1z",
      "subject": "Welcome to our newsletter!",
      "settings": null
    },
    {
      "order": 2,
      "type": "wait",
      "typeName": "Wait",
      "delayMinutes": 1440,
      "templateId": null,
      "subject": null,
      "settings": null
    },
    {
      "order": 3,
      "type": "sendEmail",
      "typeName": "SendEmail",
      "delayMinutes": null,
      "templateId": "9k4p2x7m1q3n8z",
      "subject": "Getting started with MailLand",
      "settings": null
    }
  ],
  "completionRate": 86.96,
  "createdAt": "2026-01-10T08:00:00Z",
  "updatedAt": "2026-02-17T12:00:00Z"
}

Update an Automation

PUT https://api.mail.land/v1/marketing/automations/{id}

Any status except Active can be updated (not only Draft/Paused). The request body matches the create request.

Update an automation
curl -X PUT https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome Series",
    "trigger": 0,
    "mailingListId": "7h3n5k8w2q9m1t",
    "steps": [
      {
        "type": 0, "order": 1,
        "subject": "Welcome!",
        "templateId": "2p9x4k7m3q8n1z"
      },
      { "type": 1, "order": 2, "delayMinutes": 1440 },
      {
        "type": 0, "order": 3,
        "subject": "Getting started",
        "templateId": "9k4p2x7m1q3n8z"
      }
    ]
  }'

Activate an Automation

POST https://api.mail.land/v1/marketing/automations/{id}/activate

Transitions the automation from Draft or Paused to Active. Once active, the automation begins enrolling subscribers when the trigger fires.

Activation itself is not checked against domain verification. A SendEmail step reached with no fully verified domain is held, not skipped: it is re-checked on a growing interval (15 minutes, then up to every 6 hours) and sends as soon as a domain verifies. If none verifies within 7 days of the step first becoming due, the enrolment is marked Failed (counted in totalFailed) instead of being held forever, so activating with no verified domain succeeds, but nothing delivers until you verify one, and enrolments that are still waiting after a week stop rather than hold indefinitely.

Activate an automation
curl -X POST https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s/activate \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Pause an Automation

POST https://api.mail.land/v1/marketing/automations/{id}/pause

Pauses the automation. No new subscribers are enrolled, and subscribers already in the sequence stop advancing: each enrolment holds at its current step. Activating the automation again resumes every held enrolment from where it stopped, delivering everything that fell due while paused.

Pause an automation
curl -X POST https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s/pause \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Archive an Automation

POST https://api.mail.land/v1/marketing/automations/{id}/archive

Archives the automation, which can be done from any status including Active. Archived automations are hidden from default list views.

Archiving stops both new enrolments and further steps for subscribers already enrolled: each enrolment holds at its current step, the same as Pause. Unlike pause, there is no endpoint that reactivates an Archived automation, so this hold does not resolve on its own.

Archive an automation
curl -X POST https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s/archive \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Duplicate an Automation

POST https://api.mail.land/v1/marketing/automations/{id}/duplicate?name=Copy%20of%20Welcome%20Series

Creates a new Draft automation with cloned steps and settings. The name query parameter is optional.

Duplicate an automation
curl -X POST "https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s/duplicate?name=Copy%20of%20Welcome%20Series" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Trigger Manually

POST https://api.mail.land/v1/marketing/automations/{id}/trigger
Manual trigger request
{
  "subscriberIds": ["3m8k1p9x4q7n2z", "6q2n9k4x1p8m3z", "1x7m3q8k2n9p4z"]
}

Manually enroll specific subscribers into the automation. The automation must be Active and its trigger type must be Manual (5).

Trigger automation manually
curl -X POST https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s/trigger \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"subscriberIds": ["3m8k1p9x4q7n2z", "6q2n9k4x1p8m3z", "1x7m3q8k2n9p4z"]}'

Enrollment Tracking

Each subscriber's progress through the automation is tracked as an enrollment:

StatusValueDescription
Active0Currently moving through steps
Paused1Enrollment paused
Completed2Finished all steps
Failed3A SendEmail step has no template in this tenant: missing from the start, or since deleted. Counted in the automation's totalFailed
Unsubscribed4Subscriber unsubscribed during sequence

There is no Cancelled enrollment status. The automation detail response includes aggregate enrollment counts as totalEnrolled and totalCompleted (not totalEnrollments/activeEnrollments/completedEnrollments/failedEnrollments).

Delete an Automation

DELETE https://api.mail.land/v1/marketing/automations/{id}

Permanently deletes the automation, its steps, and enrollment data.

Update and delete are allowed for any status except Active, not only Draft/Paused (update) or Draft/Archived (delete) as earlier versions of this page stated.

Delete an automation
curl -X DELETE https://api.mail.land/v1/marketing/automations/4f8k2m9p1q6z3s \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Error Responses

StatusCodeDescription
400VALIDATION_FAILEDMissing required fields or invalid step configuration: including a SendEmail step with no templateId or a Condition step with unevaluable settings
400TEMPLATE_NOT_FOUNDA SendEmail step's templateId does not resolve to a template in your tenant
400INVALID_REQUESTAutomation name already exists
403FORBIDDENInsufficient permissions
404RESOURCE_NOT_FOUNDAutomation not found, or the status transition is invalid (update/activate/pause of the wrong status, delete of an Active automation)

The automation endpoints never return 422.

Next Steps

  • Campaigns: send one-time campaigns
  • Subscribers: manage contacts that automations target
  • Lists: organize subscribers into lists for triggers

On this page