Skip to main content
A trigger fires a tool from something that happens outside the server — an inbound webhook or a schedule. A webhook verifier is what authenticates an inbound webhook over its raw bytes before its bound tool is allowed to run.

Firing a tool from a webhook

The webhook path reuses two existing pillars. An external system delivers an event to the public /universal_webhook/{topic} door; a hook bound to that topic then runs its tool in the background. So “fire a tool from a webhook” is: map a topic to a tool with a hook, and point the provider’s webhook at that topic. Because the topic rides the webhook URL as a single path segment, a hook’s name and topic must each be one URL-safe segment — ^[a-z0-9][a-z0-9_-]*$ (lowercase letters, digits, underscores, and hyphens, opening on a letter or digit; no dots or slashes). Registering a hook with a topic or name outside that charset is rejected with a 400.
A hook fires a detached tool run. An event that belongs to an existing conversation goes through the event door instead, so it enters that thread as a turn on the conversation’s own FIFO rather than as a standalone run.

Webhook verifiers

The public webhook doors — /universal_webhook/{topic} and the interactions callback — authenticate an inbound request over its raw bytes, before parsing, through a named verifier resolved from the webhook-verifier registry. A topic with no verifier binding is open by design; you bind a verifier to lock it. Verification fails closed: a signature mismatch returns a constant 401, and a missing verifier or secret returns a 500 — either way nothing is recorded and no tool runs. A verified delivery is then deduped against replay: on the /universal_webhook/{topic} ingress a delivery whose per-delivery id was already claimed within the replay window is a replay, answered with the idempotent already_seen 200 and dispatching nothing.

The built-in shared-secret verifier

The skeleton ships a shared_secret verifier that checks a named request header against a secret read from an environment variable, with a constant-time compare. A static secret carries no freshness, so its config also requires an id_header naming a header the sender fills with a unique per-delivery id, which the ingress dedupes on to refuse replays. Its config names the header, the env var holding the secret, and that id header:
config also takes an optional replay_window_seconds (a positive int, default 86400 — one day) setting how long a delivery id is remembered before the same id would be accepted again. The secret is referenced by env var name, never embedded — the binding carries no credential. Provider-specific verifiers (for example a signature scheme for a particular SaaS) ship as their own plugin packages and register the same way.

Scheduled triggers

The other trigger is time. A backend schedule fires a tool on a cadence or at a later time, naming the tool and its arguments — no external event required. A schedule may carry an optional subject in its tool arguments so a state write during the fire is keyed and attributed to the job. Unlike a hook’s key expression, a schedule’s subject is a literal {target_kind, target_name, kind, key} — the fire is anonymous, so nothing is resolved from a payload. A malformed subject is refused when the schedule is created, so a job that could never resolve its subject is never persisted. The fire records the write under door schedule with no actor. The Studio’s schedule dialog surfaces the subject as an optional group.
Every webhook door is throttled by the always-on public-door flood limiter, so an open door never ships without flood control.
See the fire-a-tool-from-a-webhook guide and the HTTP API reference for the webhook routes.