Schedules, webhooks and callbacks
An agent doesn't have to wait for a caller. A schedule runs it on a timer, a webhook runs it when another service calls a URL, and a callback sends the result of a run to your server when it finishes.
Schedules
Add one on the agent's Schedules tab, or:
lgr triggers create morning --agent support-bot \
--schedule "0 8 * * 1-5" --message "Triage the tickets that came in overnight"
Schedules are five-field cron expressions in UTC. Each time one fires, the agent's live version gets a new run in a fresh sandbox with the message, billed like any other run. A schedule missed while the platform was down fires once when it comes back.
Pausing the agent's API doesn't stop its schedules. Pause a schedule on its
own on the Schedules tab, or lgr triggers disable morning
(enable turns it back on). lgr triggers fire morning runs it now.
Webhooks
A webhook is a URL that starts a run when anything POSTs to it, such as a GitHub, Stripe or monitoring alert:
lgr triggers create alerts --agent oncall --webhook --message "New alert: {{body}}"The URL is shown once, when the webhook is created. The token in it is the
credential, so treat it like a key. {{body}} in the message is replaced
with the request body, pretty-printed when it is JSON; without a message,
the body is the message. Bodies of any content type up to 256 KB are
accepted, and the answer is 202 with the run_id. A deleted or paused
webhook answers 404.
To try one without the other service, send a sample body:
lgr triggers fire alerts --body '{"service":"checkout","error_rate":0.07}'Callbacks
Give a run, message or trigger a callback URL and its result is POSTed there when the run finishes:
lgr triggers callback morning https://example.com/hooks/lagrange
lgr run --agent support-bot --callback https://example.com/hooks/lagrange "Summarise the open tickets"The request looks like this:
POST /hooks/lagrange HTTP/1.1
content-type: application/json
user-agent: LagrangeCloud-Callbacks/1.0
lgr-delivery: dlv_3c9e01b2f7aa
lgr-event: run.completed
lgr-signature: t=1791403200,v1=5f1d…
{"id": "dlv_3c9e01b2f7aa", "type": "run.completed", "created_at": "2026-10-07T20:00:00.000Z", "run": { "id": "run_…", "status": "succeeded", "result": "…", "usage": { … }, "console_url": "…" }}type is run.completed, run.failed or run.cancelled. Answer with any
2xx. Anything else, a redirect, or no answer within 10 seconds counts as a
failure, and the same delivery is sent again after 30 seconds, 2 minutes,
10 minutes and 1 hour. Use lgr-delivery to drop duplicates.
Verifying the signature
lgr-signature is t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">,
keyed with your project's signing secret (Settings → Callbacks, or
lgr callbacks secret). Recompute it over the raw body, compare in constant
time, and reject timestamps more than five minutes old:
import { createHmac, timingSafeEqual } from "node:crypto";
// header: the lgr-signature request header; body: the raw request body.
function verify(secret, header, body) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest();
const given = Buffer.from(v1 ?? "", "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}Callback URLs must be http or https and must not point at private,
loopback or link-local addresses. Recent deliveries are under Settings →
Callbacks and lgr callbacks; lgr callbacks retry <id> sends one again
and lgr callbacks rotate replaces the signing secret.
Instances and fleets
Triggers can also message an instance or fleet
instead of starting a fresh run of an agent: use --instance NAME or
--fleet NAME in place of --agent. On an instance, the message continues
the conversation stored there.