7.11 Webhooks, schedules, and polling connectors

A pipeline runs on any verb, so an event can start one simply by arriving as a request. RS2 has three common trigger shapes: an inbound webhook, a scheduled mount, or a small connector service that calls a pipeline.

Webhooks are ordinary POSTs

Point a provider at a pipeline path and open only invoke for that unauthenticated caller:

{ "path": "/hooks", "service": "pipeline", "config": {
    "access": { "read": "A", "write": "A", "invoke": "all" }
} }

POST /hooks/stripe runs the stripe spec (or .root). Return a 2xx after the event is safely handled; most providers retry a non-2xx, giving the delivery an at-least-once shape.

Opening invoke does not mean trusting the body. Keep the signing key in the tenant's write-only secrets block, grant that name to the mount, and verify the signature in the first transform with $hmacVerify over $_rawBody. A failed verification can call $error, stopping before the event produces effects.

Scheduled mounts

Any mount can carry one schedule in its common config:

{ "path": "/poll", "service": "pipeline", "config": {
    "access": { "read": "A", "write": "A", "invoke": "A" },
    "schedule": { "every": "60s" }
} }

Or use a five-field UTC cron expression:

"schedule": { "cron": "0 9 * * *" }

Exactly one of every or cron is allowed. Intervals accept ms, s, m, and h. A bad schedule is rejected when the tenant is built.

On each tick the host sends a trusted internal POST to the mount root with x-rs2-trigger: schedule and no body. It still counts against tenant admission, limits, and the circuit breaker, and it appears in logs as an internal call. On a pipeline mount, .root handles the tick.

The scheduler does not replay missed ticks after a restart. A polling connector should keep its own cursor with durable state and ask the upstream for changes since that cursor. The standard scheduler is in-process and single-node; two independent nodes would both fire. Cluster-wide fire-once requires an operator to provide a shared ScheduleStore.

This is synchronous triggering: the sender or scheduler waits for the flow. A durable queue that absorbs bursts and retries independently is a separate, future capability, not something the schedule quietly emulates.

When declarative composition is no longer enough, Part 8 covers dropping in your own sandboxed code.


Previous: 7.10 Wrappers and response shaping · Manual home · Next: Part 8 — 8.1 When to reach for custom code →