Feature 4: Triggers and Route Dispatch
← Runtime Expression Grammar | Next: Structural Results →
Triggers are the entry points into a UWS document. They model webhook-style events, scheduled invocations, or any external signal that starts workflow execution. Once a trigger event has been accepted, UWS core owns route resolution and execution — the same orchestrator rules used for normal workflow execution apply.
Trigger Object Fields
| Field | Required | Description |
|---|---|---|
triggerId |
REQUIRED | Unique trigger identifier |
path |
optional | HTTP path for webhook-style triggers |
methods |
optional | Accepted HTTP methods |
authentication |
optional | Authentication metadata |
options |
optional | Open-shape trigger-specific options |
outputs |
REQUIRED when routes is set |
Ordered list of unique output labels |
routes |
optional | Output-to-target routing table |
Basic Webhook Trigger
A trigger that accepts POST requests and routes to different workflows based on event type:
triggers:
- triggerId: order_events
path: /webhooks/orders
methods:
- POST
authentication: bearer
outputs:
- order.created
- order.updated
- order.cancelled
routes:
- output: order.created
to: [handle_new_order]
- output: order.updated
to: [handle_order_update]
- output: order.cancelled
to: [handle_cancellation, send_refund_notification]
order.cancelled routes to two targets simultaneously — both handle_cancellation and send_refund_notification execute when that output fires.
Outputs and Uniqueness
outputs is an ordered list of unique labels the trigger may emit. Each label MUST match ^[a-zA-Z0-9._-]+$. Each invocation MUST emit exactly one label.
outputs:
- created # index 0
- updated # index 1
- deleted # index 2
Addressing Outputs by Decimal Index
A route's output may be the label itself or the zero-based decimal-string index into outputs:
routes:
- output: created # by label
to: [create_flow]
- output: "1" # index 1 = "updated"
to: [update_flow]
- output: "2" # index 2 = "deleted"
to: [delete_flow]
Both forms are equivalent. Label form is more readable; index form is useful when labels are dynamic.
Multi-Target Routing
One output can fan out to multiple targets. All targets execute through the same orchestrator rules:
routes:
- output: user.registered
to:
- onboarding_workflow # sends welcome email
- analytics_workflow # records signup event
- crm_sync_workflow # creates CRM contact
Each target receives the same trigger payload and runs independently.
Trigger with Options
options is an open-shape map for trigger-specific configuration. Its shape is entirely implementation-defined:
triggers:
- triggerId: scheduled_report
options:
schedule: "0 9 * * 1-5" # weekdays at 09:00
timezone: America/New_York
retryOnFailure: true
outputs:
- fire
routes:
- output: fire
to: [generate_daily_report]
Using Trigger Payload in Workflow Steps
During trigger-routed execution, $trigger holds the inbound payload. Steps within the routed workflow can read from it:
triggers:
- triggerId: pet_event
path: /webhooks/pets
methods: [POST]
outputs: [created]
routes:
- output: created
to: [handle_new_pet]
workflows:
- workflowId: handle_new_pet
type: sequence
steps:
- stepId: save_pet
operationRef: create_pet
request:
body:
name: $trigger.pet.name
species: $trigger.pet.species
owner: $trigger.owner.id
when: $trigger.valid == true
- stepId: notify_owner
operationRef: send_notification
request:
body:
userId: $trigger.owner.id
message: "Your pet has been registered"
Validation: What Fails
# Route references undeclared output — validation error:
routes:
- output: cancelled # not in outputs list
to: [cancel_flow]
# error: "cancelled" is not a declared trigger output
# Route with empty to — validation error:
routes:
- output: created
to: []
# error: to must contain at least one top-level stepId or workflowId
# routes set but outputs absent — validation error:
routes:
- output: created
to: [main]
# error: outputs is required when routes is set
Trigger Dispatch in Go
After accepting an event externally, dispatch it into the document:
// output is the zero-based index into trigger.outputs
// payload is the trigger event data
err := doc.DispatchTrigger(ctx, "order_events", 0, map[string]any{
"orderId": "ord-123",
"total": 99.99,
"notify": true,
})
UWS core resolves the route, validates targets, and executes them through the orchestrator. The payload becomes $trigger inside the routed workflows.
From The Big Fixture
The large fixture includes trigger routing by output labels and multi-target dispatch:
trigger "alert_webhook" {
path = "/webhooks/security/alerts"
methods = ["POST", "PUT"]
authentication = "bearer"
outputs = ["alert.received", "alert.replayed"]
route {
output = "alert.received"
to = ["main", "wf_parallel"]
}
route {
output = "alert.replayed"
to = ["step_collect_context", "wf_switch"]
}
}
Full context: testdata/big/big.hcl.