Skip to content
← All templates

Cold Email 3-Step Sequence

Send a personalized cold email, wait 3 days, check if the prospect replied, then follow up or stop. Built-in rate limiting prevents deliverability issues.

http_requestdelayrouterrate_limitloop

What this workflow does

01

Send initial email — HTTP POST to your email provider (SendGrid, Resend, Postmark) with personalized content from context. Rate-limited to respect provider quotas and protect deliverability.

02

Wait 3 days — Delay step pauses the sequence for 3 business days. The engine sleeps with zero resource consumption — no threads held, no connections open.

03

Check for reply — HTTP GET to your email provider or CRM API to check if the prospect replied. Maps reply status to context.

04

Branch on reply — Router checks if a reply was received. If yes, the sequence ends — no follow-up needed. If no, continues to follow-up.

05

Send follow-up — Second email with a different subject line and angle. Rate-limited. After the follow-up, waits 3 more days and checks again. If still no reply after the third touch, the sequence ends gracefully.

Workflow definition

Copy this JSON and POST it to /sequences.

{
  "id": "cold_email_3_step_v1",
  "context_schema": {
    "prospect_email": "string",
    "prospect_name": "string",
    "prospect_company": "string",
    "sender_name": "string",
    "sender_email": "string",
    "email_provider_url": "string",
    "reply_check_url": "string"
  },
  "blocks": [
    {
      "id": "send_initial",
      "type": "step",
      "handler": "http_request",
      "params": {
        "method": "POST",
        "url": "{{context.data.email_provider_url}}",
        "headers": {
          "Authorization": "Bearer credentials://email-api-key",
          "Content-Type": "application/json"
        },
        "body": {
          "from": "{{context.data.sender_email}}",
          "to": "{{context.data.prospect_email}}",
          "subject": "Quick question for {{context.data.prospect_company}}",
          "html": "<p>Hi {{context.data.prospect_name}},</p><p>I noticed {{context.data.prospect_company}} is scaling — curious if you've looked at automating your internal workflows.</p><p>We built Orch8, a workflow engine that handles the durability, retries, and scheduling so your team doesn't have to.</p><p>Worth a 15-minute call?</p><p>{{context.data.sender_name}}</p>"
        }
      },
      "rate_limit_key": "email:outbound",
      "retry": { "max_attempts": 2, "backoff": "30s" }
    },
    {
      "id": "wait_3_days",
      "type": "step",
      "handler": "builtin.noop",
      "delay": { "duration": 259200000 }
    },
    {
      "id": "check_reply_1",
      "type": "step",
      "handler": "http_request",
      "params": {
        "method": "GET",
        "url": "{{context.data.reply_check_url}}?email={{context.data.prospect_email}}",
        "headers": {
          "Authorization": "Bearer credentials://email-api-key"
        }
      }
    },
    {
      "id": "reply_gate_1",
      "type": "router",
      "routes": [
        {
          "condition": "steps.check_reply_1.output.replied == true",
          "blocks": [
            {
              "id": "log_replied",
              "type": "step",
              "handler": "log",
              "params": {
                "level": "info",
                "message": "Prospect {{context.data.prospect_email}} replied — sequence complete"
              }
            }
          ]
        }
      ],
      "default": [
        {
          "id": "send_followup",
          "type": "step",
          "handler": "http_request",
          "params": {
            "method": "POST",
            "url": "{{context.data.email_provider_url}}",
            "headers": {
              "Authorization": "Bearer credentials://email-api-key",
              "Content-Type": "application/json"
            },
            "body": {
              "from": "{{context.data.sender_email}}",
              "to": "{{context.data.prospect_email}}",
              "subject": "Re: Quick question for {{context.data.prospect_company}}",
              "html": "<p>Hi {{context.data.prospect_name}},</p><p>Following up — I know inboxes get busy.</p><p>Teams using Orch8 typically replace 2-3 internal tools (cron jobs, retry scripts, approval queues) with a single engine. Self-hosted, PostgreSQL-backed, runs anywhere.</p><p>Happy to show a quick demo if useful.</p><p>{{context.data.sender_name}}</p>"
            }
          },
          "rate_limit_key": "email:outbound",
          "retry": { "max_attempts": 2, "backoff": "30s" }
        },
        {
          "id": "wait_3_days_2",
          "type": "step",
          "handler": "builtin.noop",
          "delay": { "duration": 259200000 }
        },
        {
          "id": "check_reply_2",
          "type": "step",
          "handler": "http_request",
          "params": {
            "method": "GET",
            "url": "{{context.data.reply_check_url}}?email={{context.data.prospect_email}}",
            "headers": {
              "Authorization": "Bearer credentials://email-api-key"
            }
          }
        },
        {
          "id": "reply_gate_2",
          "type": "router",
          "routes": [
            {
              "condition": "steps.check_reply_2.output.replied == true",
              "blocks": [
                {
                  "id": "log_replied_2",
                  "type": "step",
                  "handler": "log",
                  "params": {
                    "level": "info",
                    "message": "Prospect {{context.data.prospect_email}} replied after follow-up"
                  }
                }
              ]
            }
          ],
          "default": [
            {
              "id": "send_final",
              "type": "step",
              "handler": "http_request",
              "params": {
                "method": "POST",
                "url": "{{context.data.email_provider_url}}",
                "headers": {
                  "Authorization": "Bearer credentials://email-api-key",
                  "Content-Type": "application/json"
                },
                "body": {
                  "from": "{{context.data.sender_email}}",
                  "to": "{{context.data.prospect_email}}",
                  "subject": "Last note — {{context.data.prospect_company}}",
                  "html": "<p>Hi {{context.data.prospect_name}},</p><p>Last touch — no hard feelings if the timing's off.</p><p>If workflow automation is ever on the roadmap, here's where to find us: <a href='https://orch8.io'>orch8.io</a></p><p>All the best,<br/>{{context.data.sender_name}}</p>"
                }
              },
              "rate_limit_key": "email:outbound"
            },
            {
              "id": "log_sequence_end",
              "type": "step",
              "handler": "log",
              "params": {
                "level": "info",
                "message": "3-step sequence completed for {{context.data.prospect_email}} — no reply"
              }
            }
          ]
        }
      ]
    }
  ]
}

Credentials to configure

email-api-keyapi_keyAPI key for your email provider (SendGrid, Resend, Postmark)

How to trigger

// POST /sequences/cold_email_3_step_v1/instances
{
  "context": {
    "data": {
      "prospect_email": "jane@example.com",
      "prospect_name": "Jane",
      "prospect_company": "Acme Corp",
      "sender_name": "Alex",
      "sender_email": "alex@yourcompany.com",
      "email_provider_url": "https://api.resend.com/emails",
      "reply_check_url": "https://api.yourapp.com/replies"
    }
  }
}

Start the sequence by creating an instance with prospect and sender details. Works with any email provider that exposes a REST API.

Customize it

Swap email provider — change the HTTP endpoint and body format for SendGrid, Postmark, or your custom SMTP relay.

Add LLM personalization — insert an llm_call step before each email that generates a personalized opening line based on the prospect's LinkedIn or company data.

CRM integration — add steps to update your CRM (HubSpot, Salesforce) with sequence status after each touch.

Business-hour send windows — add send_window to each email step to only send during 9 AM-5 PM in the prospect's timezone.

Upgrade from calendar touches to behavior

Use this sequence when a simple follow-up cadence is enough. When replies, page visits, CRM status, or product activity should change the next action, move to the event-driven template. In both cases, cancel pending messages before assigning a human owner.