LIVE DEMOSign up to bring your own.
Mode B · Triggers

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.

2 triggers seeded

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 < 60

Cron

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 ago

When 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.

Source Capability
compute-customer-health-score
when
outputs.verdict === 'watch' || outputs.verdict === 'at_risk' || outputs.score < 60
Action Capability
summarize-renewal-risk

Webhook triggerExternal system → Ctrl AI Capability

External CRM: customer-at-risk webhook

listeningfires=0

Webhook 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.

Endpoint
POST /api/webhooks/demo-acme-org/01KQW5KMCSP38GHFNFNMNN1NPS
Auth: X-Ctrl-Signature header with sha256=<hex> of HMAC-SHA256(secret, raw body). Same convention as Stripe + GitHub.
Condition (evaluated against payload + headers)
payload && payload.event === 'customer.at_risk' && (typeof payload.severity === 'number' ? payload.severity >= 3 : payload.severity === 'high')
Fires
summarize-renewal-risk
Wire from your CRM
# 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.

Production trigger admin lives behind auth at /settings/triggers; this surface is read-only by design.