Skip to content

Triggers

Triggers automatically create workflow instances in response to external events. Four trigger types are supported: HTTP webhooks, NATS message queues, file-system watch events, and in-process event bus. All triggers are persisted to the database and managed via REST API.

HTTP webhook trigger

Register a webhook trigger with a secret, then send public delivery to POST /webhooks/{slug}. The raw JSON body becomes context.data. Public delivery is always signed; the authenticated /triggers/{slug}/fireroute is for trusted API clients and internal event invocation.

Bash
POST /triggers
{
  "slug": "stripe_payment_failed",
  "sequence_name": "payment_dunning",
  "version": 1,
  "tenant_id": "acme",
  "enabled": true,
  "secret": "whsec_your_signing_secret"
}

// Disable without deleting
PATCH /triggers/stripe_payment_failed
{ "enabled": false }
POST /webhooks/stripe_payment_failed
x-trigger-timestamp: 1787443200
x-trigger-nonce: 7f6a2d85b1e94c31
x-orch8-signature: v1=<base64url-hmac>

signed_message = timestamp + "." + nonce + "." + raw_request_body
signature = "v1=" + base64url(HMAC-SHA256(secret, signed_message))

Timestamps may be at most 300 seconds old or 60 seconds in the future. Nonces are single-use, 16–128 printable ASCII characters, and the body limit is 1 MiB. Sign the exact bytes sent—do not parse and re-serialize JSON after signing.

Warning
Stripe, GitHub, and other providers use their own signature formats. Their native signatures are not interchangeable with the Orch8 scheme; use a small verification/signing gateway unless the provider can create the required Orch8 headers.

Transactional event-batch intake (current main)

PostgreSQL outbox producers can deliver up to 100 events per request toPOST /api/v1/events/batch. This endpoint is on current main after v0.7.1, not in the stable 0.7.1 release.

JSON
{
  "events": [{
    "tenant_id": "acme",
    "event_name": "order.completed",
    "producer_event_id": "outbox-018f5d2f",
    "correlation_key": "ORD-123",
    "payload": { "order_id": "ORD-123" }
  }]
}

Retries with the same tenant, event name, andproducer_event_id are deduplicated. Keep the producer outbox row until the batch response confirms acceptance.

NATS message queue trigger

Subscribe to a NATS subject. When a message arrives, the engine creates a workflow instance. The message payload becomes context.data. Configure in orch8.toml.

Bash
# orch8.toml — NATS trigger configuration
[[triggers.nats]]
subject = "events.payment.failed"
sequence_name = "payment_dunning"
tenant_id = "acme"
url = "nats://nats-server:4222"

# Multiple NATS triggers from different subjects
[[triggers.nats]]
subject = "events.user.signup"
sequence_name = "welcome_onboarding"
tenant_id = "acme"
url = "nats://nats-server:4222"

The trigger processor loop syncs trigger definitions from the database at startup and dynamically starts/stops listeners when triggers are created or disabled via API.

File watch trigger

Watch a directory (or file path) for create and modify events. When the OS notifies of a change, the engine creates a workflow instance with the file path and event type in context.data. Uses the notify crate for cross-platform fs-event support.

Bash
# orch8.toml — file watch trigger
[[triggers.file_watch]]
path = "/data/incoming"
recursive = true
sequence_name = "process_incoming_file"
tenant_id = "acme"

# context.data for each trigger:
# { "path": "/data/incoming/report.csv", "event": "create" }

# Combine with a processing sequence
{
  "type": "step", "id": "process_file",
  "handler": "csv_importer",
  "params": { "path": "{{context.data.path}}", "event": "{{context.data.event}}" }
}

Event trigger (in-process event bus)

Event triggers use an in-process event bus for trusted server-to-server or in-cluster integration. Unlike webhooks, event triggers carry no HMAC validation. They can be fired via POST /triggers/{slug}/fire or internally by workflows using the emit_event built-in handler.

Bash
POST /triggers
{
  "slug": "order_completed",
  "sequence_name": "post_order_processing",
  "tenant_id": "acme",
  "trigger_type": "event",
  "enabled": true
}

// Fire directly via API
POST /triggers/order_completed/fire
{ "order_id": "ORD-123", "total": 99.99 }
TypeScript
// Or fire from another workflow step
{ "type": "step", "id": "emit_order_event",
  "handler": "emit_event",
  "params": { "slug": "order_completed", "payload": { "order_id": "{{context.data.order_id}}" } } }

Trigger management API

Bash
# List all triggers
GET /triggers

# Get a trigger by slug
GET /triggers/{slug}

# Update trigger
PATCH /triggers/{slug}
{ "enabled": false }

# Delete a trigger
DELETE /triggers/{slug}
Was this helpful?