HeadlessOps
Examples

Multi-Step Pipeline

A multi-step pipeline with conditional routing and error handling

Multi-Step Pipeline with Conditional Routing

This example shows a pipeline that fetches a contact, routes based on their status, and handles errors gracefully.

integration.yaml

name: contact-pipeline
description: Fetch contact, route by status, send appropriate notification

trigger:
  type: manual

steps:
  - id: fetch_contact
    name: Fetch Contact
    description: Fetches contact from the CRM by email
    file: fetch_contact.ts
    credentials:
      - CRM_API_KEY
    input:
      email: "${{ trigger.body.email }}"
    on_failure: handle_error

  - id: check_status
    name: Check Status
    description: Routes based on contact status
    file: check_status.ts
    input:
      status: "${{ steps.fetch_contact.output.status }}"
    routes:
      - when: "${{ steps.check_status.output.route }} == 'active'"
        next: send_welcome
      - when: "${{ steps.check_status.output.route }} == 'churned'"
        next: send_winback
      - default: log_unknown

  - id: send_welcome
    name: Send Welcome
    description: Sends a welcome email to active contacts
    file: send_welcome.ts
    credentials:
      - SENDGRID_KEY
    input:
      email: "${{ steps.fetch_contact.output.email }}"
      name: "${{ steps.fetch_contact.output.name }}"

  - id: send_winback
    name: Send Winback
    description: Sends a win-back offer to churned contacts
    file: send_winback.ts
    credentials:
      - SENDGRID_KEY
    input:
      email: "${{ steps.fetch_contact.output.email }}"

  - id: log_unknown
    name: Log Unknown Status
    description: Logs contacts with unknown status for manual review
    file: log_unknown.ts
    input:
      contact_id: "${{ steps.fetch_contact.output.id }}"
      status: "${{ steps.fetch_contact.output.status }}"

  - id: handle_error
    name: Handle Error
    description: Catches errors from any step and sends an alert
    file: handle_error.ts
    input:
      error_message: "${{ error.message }}"
      failed_step: "${{ error.step }}"

check_status.ts

export async function run(input: Record<string, any>, ctx: StepContext) {
  const status = (input.status || "").toLowerCase();
  ctx.log("check_status", { status });

  if (status === "active" || status === "trial") {
    return { route: "active" };
  } else if (status === "churned" || status === "cancelled") {
    return { route: "churned" };
  } else {
    return { route: "unknown" };
  }
}