Orbs
An orb is an inbound webhook that wakes an agent. CI fails, an issue opens, a monitor fires — and an agent is standing there.
Setting one up
AI Agents, pick the agent, open its Webhook tab, and create one. You get a URL. Then configure it:
| Setting | What it does |
|---|---|
| Provider | generic, github or stripe — which signature scheme is checked |
| Signing secret | The provider's secret. Write-only, stored in the vault. |
| Instructions | What the agent should do when this event arrives. This is the only instruction it is given. |
| Payload fields | Dot-paths to narrow the body to. Empty passes the whole body, truncated. |
| Thread | A new conversation per delivery, or all deliveries into one thread |
| Rate limit | Accepted deliveries per hour. Defaults to 60. |
An orb without a signing secret is badged Unsigned in the list, because the URL is then the only thing authenticating the caller — treat it as a secret.
What happens to a delivery
In order, and every step fails closed:
- Size. Bodies above 1 MB are rejected. An unauthenticated route must not accept a 50 MB body just because the global upload limit allows one.
- Signature. Verified per provider. Every verifier returns false on any failure — a malformed header, a missing secret, a stale timestamp, a one-byte body change. A provider configured with no secret returns a clear error, not a degradation to "unsigned, probably fine".
- Permission check. An agent running at
yolowill not run from an unsigned orb. The real bound on a successful injection is not the fence around the payload — it is the permission mode the agent runs at, and an unauthenticated HTTP request must not reach a runtime with full host access. - Rate cap. Checked before the deduplication claim, so a throttled delivery does not consume its idempotency slot and cause the provider's legitimate retry an hour later to be dropped as a duplicate. Only accepted deliveries count toward the cap, so an orb over its limit can recover.
- Deduplication. Keyed on the provider's own delivery id, which makes it exact rather than heuristic. GitHub does not timestamp its signature, so for GitHub this dedupe is the replay guard.
- Dispatch. The agent is woken with a composed message.
Every delivery is logged — accepted or rejected, with the reason — so a webhook that is not firing can be diagnosed from the app.
The payload is untrusted
A webhook body is attacker-controlled text. The composed message has two halves, and the boundary between them is the point of the design:
- The trusted half is your instruction and a small amount of sanitised metadata.
- The untrusted half is the payload, fenced and marked as data. It is capped at 8 KiB and 40 fields, because the smaller the attacker-controlled span, the smaller the injection surface.
Narrowing the payload to the handful of fields you actually need is the single most effective thing you can do here.
Signature schemes
| Provider | Checked |
|---|---|
github | HMAC over the raw body, compared in constant time; delivery-id dedupe doubles as the replay guard |
stripe | Signed timestamp plus HMAC, within a 300-second tolerance — Stripe's own documented default |
generic | agensis's own scheme, for anything you control |
A provider is only listed once its verifier exists and is exercised by tests, because an unimplemented provider in that list would be a provider that silently accepted anything.