Skip to content

Documentation navigation

Scheduled Invocation

Run agents and swarms on cron, interval, or one-shot schedules with built-in retries, jitter, and lifecycle controls.

#Scheduled invocation

Choose between durable remote triggers and an in-process local scheduler. Remote mode is the default.

#Durable remote schedule

Python
from datetime import timedelta

from maivn import Agent

agent = Agent(name="reporter", agent_id="agent-123", version=2, api_key="...")

daily = (
    agent.cron("0 9 * * MON-FRI", tz="America/New_York")
    .key("weekday-report")
    .invoke("Prepare the weekday report")
)

hourly = (
    agent.interval(timedelta(hours=1))
    .key("hourly-health")
    .invoke("Check service health")
)

Registration is idempotent for the stable key and target. It calls the server, so examples require a deployed agent, valid credentials, and network access. Remote cron accepts UTC or an IANA timezone string; interval durations must resolve to whole seconds.

Both cron(..., name="Morning report") and interval(..., name="Hourly check") accept a display name in durable mode. You can change it later with .named(...); .key(...) remains the stable registration identity.

All durable trigger helpers inherit their owning agent's identity, including on_webhook, on_email, on_connection, on_manual, and the other event sources. By default, the agent ID is its name and the version is 1. Set agent_id and version once on the agent for a custom identity, as above; agent.serving(), agent.serve(), and maivn serve use those defaults too. Trigger declarations capture this identity when created. Per-session serving overrides do not change existing trigger targets.

Use .target("another-agent", version=3) to explicitly override a trigger's target. A standalone TriggerInvocationBuilder still requires .target(...) because it has no owning agent. Declaring a trigger does not register it; .invoke(...) or .ainvoke(...) performs registration. Give every trigger a .key(...): Studio lists a declaration without one as unfinished, and does not serve or register it until the key is added.

For example, an inbound webhook uses the same owner without repeating its ID:

Python
incoming = (
    agent.on_webhook("vault://triggers/reporter")
    .key("incoming-report")
    .invoke("Process the incoming webhook event")
)

The webhook signing secret must already exist at the given vault reference.

Use the returned Trigger for lifecycle and history operations. fire() returns a TriggerFireReceipt; fire_stream() yields the resulting runtime events.

#Local scheduler

Pass mode="local" explicitly when the current Python process should own the schedule:

Python
from maivn import Agent

agent = Agent(name="local-report", api_key="...")
job = agent.cron("0 9 * * *", mode="local").invoke("Prepare the report")
job.on_fire(lambda receipt: print(receipt.fired_at))
job.on_error(lambda receipt: print(receipt.error))

Keep the ScheduledJob alive and shut it down using its documented local lifecycle methods. Local schedules do not survive process restarts.

#Local lifetime and one-shot jobs

Local .invoke(...) starts the job immediately. The scheduler uses background threads; a short script must keep its process alive until shutdown:

Python
import time

try:
    while not job.is_done:
        time.sleep(1)
except KeyboardInterrupt:
    pass
finally:
    job.stop(drain=True, timeout=30)

agent.at(when).invoke(...) is a local one-shot job. Unlike cron and interval, at has no remote-mode option. Use a timezone-aware datetime. For local retries pass Retry(...); durable remote retries use the trigger builder's .with_options(...) instead.