Skip to content
← All templates

Workflow starter · custom workers required

Event-Driven Outreach

Start, pause, branch, and stop outreach from real lead behavior instead of arbitrary calendar touches.

Workflow sequence

  1. 01Receive intent signal
  2. 02Score and route lead
  3. 03Send first message
  4. 04Wait for reply or behavior signal
  5. 05Stop or schedule follow-up
  6. 06Assign human owner

Sequence definition

Use this sequence definition as a starting point. Set tenant_id to your tenant and use a fresh UUID for id before publishing. Implement the named business handlers in your workers; the JSON does not include provider integrations. Keep credentials in the credential store.

{
  "id": "e32825f5-41f8-510f-9d0e-8413b946079f",
  "tenant_id": "demo",
  "namespace": "default",
  "name": "event_driven_outreach_v1",
  "version": 1,
  "deprecated": false,
  "blocks": [
    {
      "type": "step",
      "id": "score",
      "handler": "score_intent",
      "params": {}
    },
    {
      "type": "step",
      "id": "send",
      "handler": "send_message",
      "params": {
        "idempotency_key": "{{context.data.lead_id}}:first"
      },
      "rate_limit_key": "sender:{{context.data.sender_id}}"
    },
    {
      "type": "step",
      "id": "wait",
      "handler": "human_review",
      "params": {},
      "wait_for_input": {
        "prompt": "Record verified lead activity",
        "timeout": 259200000,
        "store_as": "lead_activity",
        "choices": [
          {
            "label": "Reply",
            "value": "reply"
          },
          {
            "label": "High intent",
            "value": "high_intent"
          },
          {
            "label": "Follow-up due",
            "value": "followup"
          }
        ]
      }
    },
    {
      "type": "router",
      "id": "route",
      "routes": [
        {
          "condition": "context.data.lead_activity == "reply"",
          "blocks": [
            {
              "type": "step",
              "id": "handoff",
              "handler": "assign_owner",
              "params": {}
            }
          ]
        },
        {
          "condition": "context.data.lead_activity == "high_intent"",
          "blocks": [
            {
              "type": "step",
              "id": "priority",
              "handler": "notify_sales",
              "params": {}
            }
          ]
        }
      ],
      "default": [
        {
          "type": "step",
          "id": "followup",
          "handler": "send_followup",
          "params": {
            "idempotency_key": "{{context.data.lead_id}}:followup"
          }
        }
      ]
    }
  ],
  "created_at": "2026-09-05T00:00:00Z"
}

Handlers must validate required inputs and outputs. Quote extraction returns confidence; document validation returns needs_review; source archiving returns sha256. Guards read these values through outputs, and approval decisions through context.data.

Idempotency keys in params are a contract for your worker to enforce at the provider. They do not make an external write exactly-once automatically.

Steps with wait_for_input resume through an authorized human_input:{block_id} signal carrying a declared choice. A CRM or scheduling integration may supply a verified choice. Timeouts fail the wait; they do not select a follow-up or escalation branch automatically. Configure alerts and recovery for timed-out runs before production.

Review rejection skips the guarded publish or spreadsheet write. No write is authorized merely because the review step finished.

Production controls to keep

Stable idempotency keys around external writes

Explicit validation before the success state

Bounded retries with provider-aware backoff

Searchable source and destination identifiers

A named owner for exhausted retries

A replay test using duplicate input

Run this workflow

Install Orch8, connect the handlers, then test duplicate delivery and worker interruption before production.

Open the quickstart