Capabilities don’t wait to be asked.
A trigger watches for a signal — another Capability completing, a scheduled time, a webhook arriving — and fires the right Capability automatically. Same dispatcher, same audit row, same signed receipt. The agent acts on signals; humans don’t have to start every turn.
Three flavors, one runtime
Threshold
Fires when another Capability's structured output crosses a condition. Reactive — chains Capabilities into autonomous workflows.
outputs.verdict === 'watch' || outputs.score < 60Cron
Fires on a schedule. Useful for daily reconciliation passes, weekly digests, periodic compliance checks.
0 7 * * MON (every Monday at 07:00 UTC)Webhook
Fires when an external system POSTs an HMAC-signed payload. Stripe events, CRM at-risk signals, custom inbound integrations.
payload.event === 'customer.at_risk' && payload.severity === 'high'Threshold triggerCapabilities firing Capabilities
Renewal risk on low health-score
activefires=4 · last 21h agoWhen compute-customer-health-score returns a watch / at-risk verdict, automatically fire summarize-renewal-risk so the CSM has a typed renewal-risk verdict ready before they open the account.
compute-customer-health-scoreoutputs.verdict === 'watch' || outputs.verdict === 'at_risk' || outputs.score < 60summarize-renewal-riskWebhook triggerExternal system → Ctrl AI Capability
External CRM: customer-at-risk webhook
listeningfires=0Webhook that external CRMs (Salesforce, HubSpot) can POST to when an account-at-risk signal fires. The payload provides the customer narrative; the trigger condition checks payload.event === 'customer.at_risk' and the signal severity is high enough.
POST /api/webhooks/demo-acme-org/01KQW5KMCSP38GHFNFNMNN1NPSX-Ctrl-Signature header with sha256=<hex> of HMAC-SHA256(secret, raw body). Same convention as Stripe + GitHub.payload && payload.event === 'customer.at_risk' && (typeof payload.severity === 'number' ? payload.severity >= 3 : payload.severity === 'high')summarize-renewal-risk# Compute the signature locally
BODY='{"event":"customer.at_risk","severity":"high","customer":{"name":"ACME","arr":120000,"lastActivityDate":"2026-04-15"},"reason":"NPS dropped"}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | sed 's/^.* //')
curl -X POST https://ctrlai.com/api/webhooks/demo-acme-org/01KQW5KMCSP38GHFNFNMNN1NPS \
-H "Content-Type: application/json" \
-H "X-Ctrl-Signature: sha256=$SIG" \
-d "$BODY"
# 200 OK + {"ok":true,"fired":true,"firedInvocationId":"01K..."} on a match.
# Receipt anchored, Capability fires, Trust Portal updates in seconds.Same dispatcher, same audit, same proof
Whether the agent fires a Capability through /ask, a threshold trigger fires it automatically, or an external system POSTs to the webhook above — the runtime path is the same. One signed receipt per invocation. Inspect any of them in the Trust Portal.
/settings/triggers; this surface is read-only by design.