Standing work
Standing work is work that starts without anybody asking: on a schedule, or when something happens in one of the connected apps. Standing in the console is where it is watched, paused, and unblocked.
Two things feed it. Events arriving from the apps you have connected, and programs registered to run on a clock or on one of those events.
Events from an app
An app sends atmon a message when something happens in it. atmon checks the message really came from that app, trims it to a shape that is written down and does not change when the app changes, and stores it against your project.
The trimming matters more than it sounds. What you receive is the shape declared on that trigger's page in the toolkit catalog, not whatever the provider decided to send this quarter. A field the shape does not declare never reaches you.
Where the app's message identifies a person, atmon matches that against your connected accounts and puts the person on the event. Where it does not, the event still arrives, without one.
A message that fails its signature check is rejected. A message the app has already sent is recognized and not stored twice. A message nothing was subscribed to is dropped.
Subscribing to events
Register the address you want events delivered to:
const { subscription, signingSecret } =
await atmon.triggers.createSubscription({
endpointUrl: "https://acme.example/hooks/atmon",
toolkitSlug: "github",
triggerSlug: "issue_opened",
});
Leave the app out to match every app; leave the trigger out to match every trigger of one app. The address has to be HTTPS.
The signing secret is returned once and never again. atmon signs every delivery with it, and a lost secret means a new subscription rather than a recovery.
Each delivery is a POST carrying the event, with headers naming the signature, the event, this delivery, and which attempt it is. Verify the signature before you trust the body, and compare it in constant time.
Delivery is at least once. Anything other than a 2xx is a failure and is retried on a widening interval, five times, after which the delivery is set aside as dead.
Two things follow for your handler. Make it safe to receive the same event twice, keyed on the event id, because a response you sent and we did not read means the event arrives again. And answer quickly, because a slow success and a failure look the same from here.
Reading and replaying
The console lists events and their deliveries, with the attempt history on each. From code, the same four reads are on the TriggersService reference: list events, read one, list its deliveries, and replay it.
Replaying is how you recover from an outage on your side. It queues fresh deliveries to whatever subscriptions match now, and leaves the original attempt history alone, so what failed stays readable.
Programs that run on their own
A standing program is a job plan with a head on it: a clock, or an event. When the head fires, the plan runs.
The Standing screen lists them with their last run, their next run, and whether anything is blocking them. From there you can pause one, resume it, and see what it is waiting on.
A standing program that keeps running against a world that has moved is worse than one that stops, so a program can be checked against reality and paused when what it assumed no longer holds. When that happens the screen says which assumption broke, and resuming is a decision somebody makes rather than a timer.
Triggers are not tools
An event is something the app starts. A tool is something you start. The two are kept apart on purpose: triggers are left out of the search index entirely, so an assistant asking for a tool can never be handed one and try to call it.