Skip to content

Advanced Features

A/B split

Route a percentage of instances to variant A and the remainder to variant B via deterministic hash-based selection. Ideal for subject line testing, send-time experiments, and content variants.

{
  "type": "ab_split",
  "id": "subject_line_test",
  "split_key": "{{context.data.email}}",
  "variants": [
    {
      "weight": 50,
      "block": {
        "type": "step", "id": "variant_a",
        "handler": "send_email",
        "params": { "subject": "Don't miss this deal" }
      }
    },
    {
      "weight": 50,
      "block": {
        "type": "step", "id": "variant_b",
        "handler": "send_email",
        "params": { "subject": "Your exclusive offer expires soon" }
      }
    }
  ]
}

The split_key is hashed deterministically — the same contact always gets the same variant, enabling reliable attribution and consistent user experience.

Circuit breaker

Per-handler circuit breaker protects downstream services from being overwhelmed by a cascade of failures. Configured per handler name.

  • ClosedNormal operation — tasks dispatched to the handler
  • OpenHandler tripped — tasks fail immediately without dispatching (configurable cooldown period)
  • HalfOpenCooldown expired — one probe task dispatched; success closes, failure re-opens
# Inspect circuit breakers
GET /circuit-breakers

# Reset a tripped breaker
POST /circuit-breakers/{handler_name}/reset

SLA timers and deadlines

Attach a wall-clock deadline to any step. If the step has not completed by the deadline, an escalation handler fires automatically.

{
  "type": "step",
  "id": "resolve_ticket",
  "handler": "wait_for_resolution",
  "deadline": "4h",
  "on_deadline_breach": {
    "handler": "escalate_to_manager",
    "params": {
      "ticket_id": "{{context.data.ticket_id}}",
      "message": "SLA breach: ticket unresolved after 4 hours"
    }
  }
}

Workflow interceptors

Attach lifecycle hooks to a sequence definition. Interceptors fire at configurable points without modifying the sequence logic.

// Part of the sequence definition
"interceptors": [
  {
    "on": "before_step",
    "handler": "audit_logger",
    "params": { "namespace": "{{context.config.namespace}}" }
  },
  {
    "on": "on_failure",
    "handler": "notify_oncall",
    "params": { "channel": "pagerduty" }
  },
  {
    "on": "on_complete",
    "handler": "update_crm",
    "params": { "status": "sequence_done" }
  }
]

Valid on values: before_step, after_step, on_signal, on_complete, on_failure.

Encryption at rest

Encrypt instance metadata and context at the field level using AES-256-GCM. Configure a 32-byte hex key and all new instances are stored encrypted.

# Environment variable
ORCH8_ENCRYPTION_KEY=your-32-byte-hex-key-here

# Or in orch8.toml
[security]
encryption_key = "your-32-byte-hex-key-here"

Encryption is transparent to handlers — decryption happens before the handler receives the context.

Audit log

Every state transition is recorded in an append-only audit_log table. Query the full history for any instance via the REST API.

GET /instances/{id}/audit

// Response
[
  { "event": "instance_created", "at": "2024-01-15T10:00:00Z" },
  { "event": "step_started", "block_id": "send_email", "at": "2024-01-15T10:00:01Z" },
  { "event": "step_completed", "block_id": "send_email", "at": "2024-01-15T10:00:02Z" },
  { "event": "instance_waiting", "reason": "delay_3d", "at": "2024-01-15T10:00:02Z" }
]

Debug mode

Set breakpoints on step IDs in instance metadata. The engine pauses execution at each breakpoint, allowing step-through inspection via REST or CLI. Resume with a Resume signal.

// Enable breakpoints at creation
{
  "metadata": {
    "_debug_breakpoints": ["send_email", "check_reply"]
  }
}
// Inspect state
GET /instances/{id}

// Step forward
POST /instances/{id}/signals
{ "signal_type": "resume" }