Skip to main content

Automate a Flow with Triggers

A Trigger binds a starter (manual, webhook, schedule) to a target (orchestration, agent, tool). Each firing is an auditable firing record run under an identity derived from the trigger's creator. You bind one orchestration to all three starters.

Prerequisites​

export SOAT_BASE_URL=http://localhost:5047

Step 1 — Log in as admin​

See IAM — Authentication.

soat login-user --username admin --password Admin1234!
soat configure
# Token: <paste token here>

Step 2 — Create a project​

Every resource lives inside a Project.

PROJECT_ID=$(soat create-project --name "Triggers Tutorial" | jq -r '.id')
echo "Project: $PROJECT_ID"

Step 3 — Create an orchestration to activate​

A single-transform Orchestration runs synchronously without an AI provider.

ORCH_ID=$(soat create-orchestration \
--project-id "$PROJECT_ID" \
--name "daily-cycle" \
--nodes '[{"id":"seed","type":"transform","expression":{"var":"input.cycle"},"state_mapping":{"state.cycle":{"var":"output.result"}}}]' \
--edges '[]' | jq -r '.id')
echo "Orchestration: $ORCH_ID"

Step 4 — Create a manual trigger​

input is the Trigger's static input, shallow-merged under each firing's runtime input. Creating a trigger requires the target-start permission (orchestrations:StartRun) — Triggers — Run-as Identity.

TRIGGER_ID=$(soat create-trigger \
--project-id "$PROJECT_ID" \
--name "run-daily-cycle" \
--type manual \
--target-type orchestration \
--target-id "$ORCH_ID" \
--input '{"cycle":"daily"}' | jq -r '.id')
echo "Trigger: $TRIGGER_ID"

Step 5 — Fire the trigger and inspect the firing​

A manual fire runs the target synchronously and returns a terminal firing record; fire-time input is merged over the static input.

FIRING_ID=$(soat fire-trigger --trigger-id "$TRIGGER_ID" \
--input '{"cycle":"2025-01-01"}' | jq -r '.id')
echo "Firing: $FIRING_ID"

# Inspect the terminal firing record (status is completed/failed).
soat get-trigger-firing --firing-id "$FIRING_ID"

# List all firings for this trigger.
soat list-trigger-firings --trigger-id "$TRIGGER_ID"

Step 6 — Schedule the same flow with cron​

cron is a strict 5-field UTC expression; the server computes next_fire_at and a poller fires due triggers exactly once, coalescing missed occurrences. See Triggers — Schedules and Misfire Coalescing.

SCHEDULE_ID=$(soat create-trigger \
--project-id "$PROJECT_ID" \
--name "daily-cycle-8am" \
--type schedule \
--target-type orchestration \
--target-id "$ORCH_ID" \
--cron "0 8 * * *" \
--input '{"cycle":"scheduled"}' | jq -r '.id')

# next_fire_at is server-computed in UTC.
soat get-trigger --trigger-id "$SCHEDULE_ID"

The scheduler fires it; you do not.


Step 7 — Trigger from an inbound webhook​

An external system POSTs to a public inbound endpoint. Creation returns a signing secret (also via get-trigger-secret; replace with rotate-trigger-secret). See Triggers — Inbound Webhook Endpoint; the outbound counterpart is Webhooks.

WEBHOOK_TRIGGER_ID=$(soat create-trigger \
--project-id "$PROJECT_ID" \
--name "cycle-on-webhook" \
--type webhook \
--target-type orchestration \
--target-id "$ORCH_ID" | jq -r '.id')

# Retrieve the current signing secret (webhook triggers only).
soat get-trigger-secret --trigger-id "$WEBHOOK_TRIGGER_ID"

# Rotate it when it may have leaked.
soat rotate-trigger-secret --trigger-id "$WEBHOOK_TRIGGER_ID"

POST /hooks/triggers/{trigger_id} is made by the external system with no bearer token; it is authenticated by the X-Soat-Signature HMAC header over the raw body.


Next Steps​

The same pattern works with target_type: agent and target_type: tool. To ship a trigger with its target, declare a trigger resource in a Formation template — Triggers — Formation Support.