Triggers & schedules
Triggers are how an agent acts when nobody is messaging it: on a schedule, or when an external system calls in. They fire from the platform, so they work even while the agent is asleep.
Why triggers exist
Maritime agents sleep when idle, and timers inside a sleeping agent do not fire. A cron library or setInterval running inside the process dies with the process. Triggers live outside the agent: the platform holds the schedule, wakes the agent (about a second), and delivers the event as a message. The schedule keeps its promises even when the agent does not exist between ticks.
Cron triggers
A cron trigger takes a standard five-field expression, wakes the agent on schedule, and delivers its prompt as a chat message on every fire. Three ways to create one:
- Ask the agent (OpenClaw-family and Hermes templates): tell it "send me a digest every weekday at 9" and the platform syncs the schedule it sets for itself into real wake triggers. These templates have no trigger editor in the dashboard; the agent is the editor.
- The dashboard editor (Claude Code, DeepSeek Harness, and custom containers): the agent page's trigger panel takes the cron expression; set the message to deliver via the API's
config.prompt. - The CLI, for any framework:
maritime triggers create my-agent --type cron --cron "0 9 * * 1-5" --prompt "Send the daily digest". Without--promptthe trigger only wakes the agent.
# five-field cron
0 9 * * 1-5 # weekdays at 09:00
*/15 * * * * # every 15 minutes
0 0 1 * * # first of the month, midnightComing from another provider? EventBridge's six-field cron(...), Azure's NCRONTAB with seconds, and rate(...) expressions all map onto five-field cron; the migration guides show the conversions per provider.
Webhook triggers
Every agent has a public invoke URL. A POST with a JSON body wakes the agent (if asleep) and delivers the body as a message. Authenticate with the agent's invoke token, in the X-Maritime-Webhook-Token header or as ?token=. Calls without a valid token are still accepted for now and logged; they will be refused once enforcement turns on, so send the token today. Each caller IP gets 30 calls per minute per agent.
POST https://api.maritime.sh/api/webhooks/{agent_id}
X-Maritime-Webhook-Token: <token>
Content-Type: application/json
# Owner-only: read the token (returns token, header name, and a ready-to-use URL)
GET https://api.maritime.sh/api/webhooks/{agent_id}/token
# Replace it with a new secret; the old token stops working at once
POST https://api.maritime.sh/api/webhooks/{agent_id}/token/rotatePoint CI failures, form submissions, or any external system at it and the agent decides what the event means. Channel messages (Telegram, Discord) wake agents the same way; see Channels & integrations.
Listing an agent's triggers
maritime triggers list my-agent # cron / webhook / telegram / discordPublishing schedules from a custom agent
An agent you wrote yourself (a custom container) can publish its own schedule instead of having you click triggers together in the dashboard. Either serve GET /schedules (Maritime polls while the agent is awake and registers real wake triggers), or push the list with one SDK line (observeScheduler / observe_scheduler). Maritime wakes the VM about 10 seconds before each occurrence; an entry that carries a prompt instead fires at the occurrence itself and has the prompt delivered to POST /chat with source "scheduled", even when the agent is already awake.
{"id": "morning", "cron": "35 9 * * *", "tz": "America/New_York",
"prompt": "Send the morning summary", "enabled": true}Details and the SDK helpers are in SDK: scheduled wakes.
If you really need an in-process scheduler
Every machine sleeps when idle, and timers inside a sleeping machine don't fire. Keep a scheduler in your code only for work that runs while the agent is already awake; for everything that must happen on time, use a trigger: it wakes the agent and survives restarts.