For AI agents: a documentation index is available at https://docs.plungeai.com/llms.txt. Append .md to any page URL, or send Accept: text/markdown, to get markdown. Setup instructions for agents are at https://docs.plungeai.com/agents.md. Execution planes take an ozk_ key; the models plane takes an sk-ocean- key.

Documentation Index: fetch the complete documentation index at /llms.txt. Use this file to discover all available pages before exploring further.

The scheduler runs platform work unattended on cron expressions: a saved workflow every morning, an agent query hourly, a pre-built agent card nightly. Jobs, run history, retries, and stats are first-class — a scheduled job is not fire-and-forget; every run is logged and resolvable like any other execution.

Creating and managing jobs — plungeai_schedule

One MCP tool, action-routed:

plungeai_schedule {action: "create",
  name: "Daily market brief",
  job_type: "workflow",            # agent | query | workflow | heartbeat
  target: "<workflow id>",
  schedule: "0 7 * * *"            # 07:00 UTC daily
}

plungeai_schedule {action: "list"}
plungeai_schedule {action: "get", job_id: "…"}
plungeai_schedule {action: "update", job_id: "…", schedule: "0 8 * * 1-5"}
plungeai_schedule {action: "pause",  job_id: "…"}
plungeai_schedule {action: "resume", job_id: "…"}
plungeai_schedule {action: "run_now", job_id: "…"}   # returns the run's REAL outcome + execution id
plungeai_schedule {action: "runs", job_id: "…"}      # execution history
plungeai_schedule {action: "stats"}
plungeai_schedule {action: "delete", job_id: "…"}

Also on create: mission_ref schedules a pre-built agent card directly (a bounded mission on cron — see plungeai-missions), and parameters carries the run input.

Heartbeat jobs — watch a condition, act only when it trips

job_type: "heartbeat" is a user-facing job kind (not just internal plumbing): each tick it runs check_agent against condition_prompt and only acts when the condition is met. Create requires name, schedule, check_agent, condition_prompt; optional actions: trigger_workflow (a workflow id to fire) and notify_channel (telegram | whatsapp | discord | slack | email) + notify_chat_id. Heartbeat notify routes through the same per-channel delivery adapters as ordinary scheduled-result delivery — telegram/discord/slack/email all deliver (subject to each channel's normal pairing/confirmation fence, see plungeai-campaigns' delivery-channel table); whatsapp has a known gap — the heartbeat payload never carries the phone_number_id the whatsapp adapter requires, so a whatsapp target silently no-ops today.

plungeai_schedule {action: "create", job_type: "heartbeat",
  name: "Price watch", schedule: "*/30 * * * *",
  check_agent: "<agent id from a live search>",
  condition_prompt: "Is BTC below $50k? Answer MET or NOT MET with evidence.",
  notify_channel: "telegram", notify_chat_id: "…"}

Heartbeat condition checks run memory-free — they are watch-and-notify ticks, not learning runs.

Semantics you should rely on:

  • Workflow jobs run by reference — the scheduler stores a pointer, not a YAML copy. Edit the workflow in Studio and the next scheduled run uses the latest version automatically. Never "update the schedule" to change workflow logic.
  • Agent and query jobs are auto-wrapped at create time into a saved one-task workflow (Scheduled: <name>) and scheduled as workflow jobs — so their runs get full standard execution history too.
  • Delete is soft: history stays readable; a deleted job refuses run_now/update/pause/resume. There is no undelete — create a new job.
  • run_now returns the real outcome plus a resolvable execution id — use it to verify a job end-to-end right after creating it (always do this).

Cron expressions

Standard 5-field cron, minimum granularity 1 minute, evaluated in UTC:

minute hour day month day-of-week
IntentExpression
Every 5 minutes*/5 * * * *
Hourly on the hour0 * * * *
Daily 07:000 7 * * *
Weekdays 09:000 9 * * 1-5
Mondays 09:000 9 * * 1
1st of month 00:000 0 1 * *

, lists, - ranges, / steps, * any. Cron accuracy is ±5 seconds — do not build designs that need tighter timing.

@once and runAgain — one-time and self-scheduling jobs

@once is a valid schedule that fires exactly once, at the job's explicit next-run time, then soft-deletes itself (run history survives). A failed @once run may retry once with the standard +5 min delay before its final deletion.

runAgain is the mechanic behind a campaign that re-schedules itself without a cron expression: the last row of its per-cycle pipeline is agent: scheduler, params: {operation: "runAgain", in: "1h"}. It creates exactly ONE @once job for that workflow (never stacks — an existing pending @once job for the same workflow is re-timed, not duplicated). in accepts a duration (5m, 1h, 90s) or a number of seconds, default 1h. This is orthogonal to a campaign scheduled with a cron expression, which is driven by an ordinary recurring job instead — see plungeai-campaigns.

Date tokens — schedules that reason about time windows

Scheduled workflows should never bake concrete dates into YAML. The engine seeds rolling tokens from each run's execution time — same {placeholder} syntax as inputs, lower precedence than caller inputs:

{now} {today} {yesterday} {week_start} {week_end} {last_week_start} {last_week_end} {month_start} {month_end} {last_month_start} {last_month_end} — all UTC, ISO weeks (Monday = day 1).

- type: task
  id: compare
  agent: llm-agent
  prompt: |
    Compare this week ({week_start} to {week_end}) against last week
    ({last_week_start} to {last_week_end}). Identify the notable moves.

The scheduler passes the execution time automatically — a job scheduled "Monday 09:00" reasons about the correct ISO week every time, forever, with zero YAML edits.

Runs, retries, and history

  • Every run is logged: status (running → completed/failed), start/end, duration, result pointer, error message, attempt number, what triggered it (schedule vs manual).
  • Failed runs auto-retry: up to 3 attempts by default, 5 minutes apart; after max retries the job returns to its normal schedule (it is not paused by failure). The per-job max_retries override is settable only on the REST/Studio surface (POST /jobs) — plungeai_schedule has no such field.
  • Retry re-fires the full job — schedule only idempotent work, or make the workflow itself idempotent (e.g. keyed upserts), for anything with side effects.
  • action: "runs" lists history; its Execution ID column is the id to fetch output with plungeai_get_result / inspect with plungeai_get_workflow_status. A scheduler run id itself is NOT resolvable by those tools — both will tell you to go through runs first; do that instead of retrying.
  • action: "stats" summarizes: total/active/paused jobs, today's runs and success/failure split, average duration — the first read when "schedules seem broken".

Memory semantics of scheduled runs

A scheduled user workflow keeps the FULL per-user memory lifecycle — it recalls and writes long-term memory exactly like a manual run (owner decision; see plungeai-memory). Only system jobs and heartbeat condition checks run memory-free. Design accordingly: a daily mission genuinely accumulates knowledge run over run.

Patterns

  • Morning brief: workflow (parallel news/search fan-out → synthesis) on 0 7 * * *, date tokens for "since yesterday".
  • Weekly comparison report: workflow with {last_week_*} vs {week_*} windows on 0 9 * * 1.
  • Standing watchdog: quick-effort mission card via mission_ref hourly; it checks a condition and only escalates (writes/notifies) when triggered.
  • Data hygiene: batch workflow nightly over a table; opt in with track: true on the batch so re-runs resume instead of repeat (plungeai-workflows).
  • Campaign cadence: a list-to-completion campaign is driven either by an ordinary recurring cron job or by its own runAgain self-scheduling — see plungeai-campaigns.

Troubleshooting

  1. Job not firing → get the job: status must be active and next run in the future; then runs for the last attempt's error.
  2. Run failed → runs → take the Execution ID → plungeai_get_workflow_status (which also self-heals stuck runs) and the trace (plungeai-results-traces).
  3. Wrong data window → check UTC: 0 7 * * * is 07:00 UTC, not local; date tokens are UTC/ISO too.
  4. Output looks stale → the job runs the workflow by reference; confirm which version is saved (someone may have edited it — that IS the version that runs).

MCP tool: plungeai_schedule

Purpose: cron-scheduled jobs — run agents, workflows, or condition-check heartbeats on a schedule. Jobs are ownership-scoped.

Actions

ActionRequiresNotes
stats—Job counts by status.
list—Table: Name, Type, Schedule, Status, Next run, ID.
getjob_idFull job JSON.
createsee belowFour job shapes (below).
updatejob_id (+ any of name/description/schedule/target/parameters)Deleted jobs refuse it.
pause / resumejob_idToggle paused/active.
deletejob_idSOFT delete — run history stays readable via runs; deleted jobs refuse run_now/update/pause/resume.
run_nowjob_idFire immediately; reports the run's REAL outcome and a resolvable execution id.
runsoptional job_idRun history table; its Execution ID column is what plungeai_get_result accepts.

Create — the four shapes

  1. Workflow job: job_type: "workflow", target: "<workflow_id you own>", name, schedule (cron). The workflow must exist in the caller's account.
  2. Agent job: job_type: "agent", target: "<agent-id>", parameters: {"prompt": "what to do each run"}, name, schedule. The agent is fence-checked NOW (unknown/inactive refused at create, not at fire time), and the job is auto-wrapped into a visible saved workflow ("Scheduled: <name>") so runs land in normal execution history.
  3. Query job: job_type: "query", target is JSON: {"agent": "<agent-id>", "query": "<what to run>"} — same wrapping and fence as agent jobs.
  4. Heartbeat: job_type: "heartbeat", plus check_agent, condition_prompt (required), optional trigger_workflow (workflow id to fire when the condition is met), notify_channel (telegram|whatsapp|discord|slack|email) + notify_chat_id. Each tick it checks the condition and only acts when met. Delivery runs through the same per-channel adapters as regular scheduled-result delivery — telegram, discord, slack, and email all deliver; ⚠️ whatsapp is a known gap (the heartbeat payload doesn't carry the phone_number_id the adapter requires, so it silently no-ops) — prefer trigger_workflow or another channel for a whatsapp-bound notice.

Also on create: mission_ref: "<agent-card-id>" (+ name, schedule, optional parameters.prompt) schedules a pre-built agent card — the card resolves LIVE at fire time, so card edits apply to future runs automatically.

Example

{"user_request": "every weekday at 7am send me an AI news brief",
 "action": "create", "name": "Morning AI brief", "job_type": "agent",
 "target": "brave-agent", "schedule": "0 7 * * 1-5",
 "parameters": {"prompt": "Top AI news of the last 24h, with links"}}

Failures & fixes

  • "create requires: name, job_type (agent|query|workflow), target, schedule" → supply them (heartbeat/mission_ref list their own requireds).
  • Fence refusal on target/check_agent → re-discover with plungeai_list_agents; never retry the same id.
  • "Agent schedules need a prompt" → add parameters: {"prompt": ...}.
  • "No workflow <id> found in your account" → pass an id from plungeai_list_workflows or build one first.
  • "That looks like an execution id (exec_...), not a job id (job_...)" → execution ids identify single runs; use action: "runs" to list them, or pass the job id.
  • "The scheduler's storage is briefly busy... Retry in a few seconds." → transient; the job most likely still exists — do NOT treat as not-found.
  • Scheduler error (HTTP <status>). (e.g. Scheduler error (HTTP 401).) → the gateway→scheduler hop itself failed; not your arguments. Report it rather than retry-looping.
  • To fetch a scheduled run's output: action: "runs" → take the Execution ID column → plungeai_get_result {workflow_id: <that id>}.

Planned: TI-33

Search is not available yet. Until it ships, use the page index or browse the sidebar.

Planned: TI-34

The docs assistant is not available yet. You can hand these docs to your own assistant instead.